Skip to content

Commit

Permalink
Modify custom_file_dialog, portable_window, propertly_list, and text_…
Browse files Browse the repository at this point in the history
…editor examples to be compatible with the newest raylib and raygui (#156)

Modify gui_textbox_extended.h to contain DrawTextRec and DrawTextRecEx since they were cut from raylib, also add DrawTextBoxedSelectable as an alias for DrawTextRecEx for compatibility/name consistency
Modify gui_textbox_extended.h to be compatible with the latest raylib
  • Loading branch information
Vortetty committed Sep 19, 2021
1 parent c1af7c3 commit e81fd89
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 30 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ Styles can be loaded at runtime using raygui `GuiLoadStyle()` function. Simple a

## raygui icons

`raygui` supports custom icons provided as an external array of data. To support icons just define `RAYGUI_SUPPORT_ICONS` before including `raygui`.
`raygui` supports custom icons provided as an external array of data. To support icons just define `RAYGUI_SUPPORT_RICONS` before including `raygui`.

A set of custom handcrafted icons is provided in [`ricons`](src/ricons.h). This set of icons can be created and customized using [rGuiIcons](https://raylibtech.itch.io/rguiicons) tool.

<img align="right" src="images/raygui_ricons.png">

```c
#define RAYGUI_IMPLEMENTATION
#define RAYGUI_SUPPORT_ICONS
#define RAYGUI_SUPPORT_RICONS
#include "raygui.h"
```
To use any of those icons in your gui, just preprend *iconId* to any text written within `raygui` controls:
Expand All @@ -71,7 +71,7 @@ if (GuiButton(rec, GuiIconText(RICON_FILE_OPEN, "Open Image"))) { /* ACTION */ }

`raygui` is intended to be used as a portable library to be integrated in code form into the target project but some users could require a shared/dynamic version of the library, for example, to create bindings. In that case, `raygui` can be built as a shared library using:
```
mv src/raygui.h src/raygui.c && gcc -shared -fpic -DRAYGUI_SUPPORT_ICONS -DRAYGUI_IMPLEMENTATION -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 src/raygui.c -o raygui.so
mv src/raygui.h src/raygui.c && gcc -shared -fpic -DRAYGUI_SUPPORT_RICONS -DRAYGUI_IMPLEMENTATION -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 src/raygui.c -o raygui.so
```

license
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_file_dialog/custom_file_dialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include "raylib.h"

#define RAYGUI_IMPLEMENTATION
#define RAYGUI_SUPPORT_ICONS
#define RAYGUI_SUPPORT_RICONS
#include "../../src/raygui.h"

#undef RAYGUI_IMPLEMENTATION // Avoid including raygui implementation again
Expand Down
2 changes: 1 addition & 1 deletion examples/portable_window/portable_window.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include "raylib.h"

#define RAYGUI_IMPLEMENTATION
#define RAYGUI_SUPPORT_ICONS
#define RAYGUI_SUPPORT_RICONS
#include "../../src/raygui.h"

//------------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions examples/property_list/dm_property_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ double GuiDMSpinner(Rectangle bounds, double value, double minValue, double maxV
GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);

#if defined(RAYGUI_SUPPORT_ICONS)
#if defined(RAYGUI_SUPPORT_RICONS)
if (GuiButton(leftButtonBound, GuiIconText(RICON_ARROW_LEFT_FILL, NULL))) value -= step;
if (GuiButton(rightButtonBound, GuiIconText(RICON_ARROW_RIGHT_FILL, NULL))) value += step;
#else
Expand All @@ -417,7 +417,7 @@ double GuiDMSpinner(Rectangle bounds, double value, double minValue, double maxV


void GuiDMPropertyList(Rectangle bounds, GuiDMProperty* props, int count, int* focus, int* scrollIndex) {
#ifdef RAYGUI_SUPPORT_ICONS
#ifdef RAYGUI_SUPPORT_RICONS
#define PROPERTY_COLLAPSED_ICON "#120#"
#define PROPERTY_EXPANDED_ICON "#121#"
#else
Expand Down
2 changes: 1 addition & 1 deletion examples/property_list/property_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include "raylib.h"

#define RAYGUI_IMPLEMENTATION
#define RAYGUI_SUPPORT_ICONS
#define RAYGUI_SUPPORT_RICONS
#include "../../src/raygui.h"

#undef RAYGUI_IMPLEMENTATION // Avoid including raygui implementation again
Expand Down
16 changes: 8 additions & 8 deletions examples/text_editor/text_editor.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "raylib.h"

#define RAYGUI_IMPLEMENTATION
#define RAYGUI_SUPPORT_ICONS
#define RAYGUI_SUPPORT_RICONS
#include "../../src/raygui.h"


Expand Down Expand Up @@ -131,7 +131,7 @@ bool GuiTextEditor(Rectangle bounds, char *text, int textSize, bool editMode)
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 codepointCount = GetCodepointCount(text);

int textLen = strlen(text); // Text length in bytes

Expand Down Expand Up @@ -214,8 +214,8 @@ bool GuiTextEditor(Rectangle bounds, char *text, int textSize, bool editMode)
int codepoint = GetCodepoint(&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,
Rectangle rec = { bounds.x + textOffsetX + font.glyphs[index].offsetX*scaleFactor,
bounds.y + textOffsetY + font.glyphs[index].offsetY*scaleFactor,
font.recs[index].width*scaleFactor, font.recs[index].height*scaleFactor };

// Automatic line break to wrap text inside box
Expand All @@ -225,8 +225,8 @@ bool GuiTextEditor(Rectangle bounds, char *text, int textSize, bool editMode)
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,
rec = (Rectangle){ bounds.x + textOffsetX + font.glyphs[index].offsetX*scaleFactor,
bounds.y + textOffsetY + font.glyphs[index].offsetY*scaleFactor,
font.recs[index].width*scaleFactor, font.recs[index].height*scaleFactor };
}

Expand Down Expand Up @@ -280,8 +280,8 @@ bool GuiTextEditor(Rectangle bounds, char *text, int textSize, bool editMode)
// 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));
if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING));
else textOffsetX += ((float)font.glyphs[index].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING));

i += (codepointByteCount - 1); // Move text bytes counter to next codepoint
cp++;
Expand Down
171 changes: 157 additions & 14 deletions src/gui_textbox_extended.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

#ifndef GUI_TEXTBOX_EXTENDED_H
#define GUI_TEXTBOX_EXTENDED_H

//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
Expand Down Expand Up @@ -78,6 +77,10 @@ RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int

RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool editMode);

RAYGUIDEF static void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits
RAYGUIDEF static void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection
RAYGUIDEF static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Alias for above

#ifdef __cplusplus
}
#endif
Expand All @@ -96,6 +99,146 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi
#include "raygui.h"
#endif

// Draw text using font inside rectangle limits
static void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
{
DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE);
}

// Draw text using font inside rectangle limits with support for text selection
static void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
{
int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop

float textOffsetY = 0; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw

float scaleFactor = fontSize/(float)font.baseSize; // Character rectangle scaling factor

// Word/character wrapping mechanism variables
enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
int state = wordWrap? MEASURE_STATE : DRAW_STATE;

int startLine = -1; // Index where to begin drawing (where a line begins)
int endLine = -1; // Index where to stop drawing (where a line ends)
int lastk = -1; // Holds last value of the character position

for (int i = 0, k = 0; i < length; i++, k++)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepoint(&text[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);

// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
if (codepoint == 0x3f) codepointByteCount = 1;
i += (codepointByteCount - 1);

float glyphWidth = 0;
if (codepoint != '\n')
{
glyphWidth = (font.glyphs[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.glyphs[index].advanceX*scaleFactor;

if (i + 1 < length) glyphWidth = glyphWidth + spacing;
}

// NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
// We store this info in startLine and endLine, then we change states, draw the text between those two variables
// and change states again and again recursively until the end of the text (or until we get outside of the container).
// When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
// and begin drawing on the next line before we can get outside the container.
if (state == MEASURE_STATE)
{
// TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
// Ref: http://jkorpela.fi/chars/spaces.html
if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;

if ((textOffsetX + glyphWidth) > rec.width)
{
endLine = (endLine < 1)? i : endLine;
if (i == endLine) endLine -= codepointByteCount;
if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);

state = !state;
}
else if ((i + 1) == length)
{
endLine = i;
state = !state;
}
else if (codepoint == '\n') state = !state;

if (state == DRAW_STATE)
{
textOffsetX = 0;
i = startLine;
glyphWidth = 0;

// Save character position when we switch states
int tmp = lastk;
lastk = k - 1;
k = tmp;
}
}
else
{
if (codepoint == '\n')
{
if (!wordWrap)
{
textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
textOffsetX = 0;
}
}
else
{
if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width))
{
textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
textOffsetX = 0;
}

// When text overflows rectangle height limit, just stop drawing
if ((textOffsetY + font.baseSize*scaleFactor) > rec.height) break;

// Draw selection background
bool isGlyphSelected = false;
if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
{
DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
isGlyphSelected = true;
}

// Draw current character glyph
if ((codepoint != ' ') && (codepoint != '\t'))
{
DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
}
}

if (wordWrap && (i == endLine))
{
textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
textOffsetX = 0;
startLine = endLine;
endLine = -1;
glyphWidth = 0;
selectStart += lastk - k;
k = lastk;

state = !state;
}
}

textOffsetX += glyphWidth;
}
}

static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint) {
DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, selectStart, selectLength, selectTint, selectBackTint);
}

//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
Expand Down Expand Up @@ -178,9 +321,9 @@ RAYGUIDEF void GuiTextBoxSetSelection(int start, int length)
RAYGUIDEF Vector2 GuiTextBoxGetSelection(void)
{
if (guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor) return RAYGUI_CLITERAL(Vector2){ 0 };
else if (guiTextBoxState.cursor > guiTextBoxState.select) return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.select, guiTextBoxState.cursor - guiTextBoxState.select };
else if (guiTextBoxState.cursor > guiTextBoxState.select) return RAYGUI_CLITERAL(Vector2){ (float)guiTextBoxState.select, (float)guiTextBoxState.cursor - guiTextBoxState.select };

return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.cursor, guiTextBoxState.select - guiTextBoxState.cursor };
return RAYGUI_CLITERAL(Vector2){ (float)guiTextBoxState.cursor, (float)guiTextBoxState.select - guiTextBoxState.cursor };
}

// Returns true if a textbox control with specified `bounds` is the active textbox
Expand Down Expand Up @@ -436,7 +579,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi
Rectangle textRec = { bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING),
bounds.y + verticalPadding + GuiGetStyle(TEXTBOX, BORDER_WIDTH),
bounds.width - 2*(GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) + GuiGetStyle(TEXTBOX, BORDER_WIDTH)),
GuiGetStyle(DEFAULT, TEXT_SIZE) };
(float)GuiGetStyle(DEFAULT, TEXT_SIZE) };

Vector2 cursorPos = { textRec.x, textRec.y }; // This holds the coordinates inside textRec of the cursor at current position and will be recalculated later
bool active = GuiTextBoxIsActive(bounds); // Check if this textbox is the global active textbox
Expand Down Expand Up @@ -806,7 +949,7 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi

if (pressed) framesCounter = 0;
}

// Draw control
//--------------------------------------------------------------------
DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha));
Expand All @@ -816,9 +959,9 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi
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_FOCUSED)), guiAlpha));

// Draw blinking cursor
if (editMode && active && ((framesCounter/TEXTEDIT_CURSOR_BLINK_FRAMES)%2 == 0) && selLength == 0)
if (editMode && active && ((framesCounter/30)%2 == 0) && selLength == 0)
{
DrawRectangle(cursorPos.x, cursorPos.y, 1, GuiGetStyle(DEFAULT, TEXT_SIZE)*2, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
DrawRectangle(cursorPos.x, cursorPos.y-1, 1, GuiGetStyle(DEFAULT, TEXT_SIZE)+2, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
}
}
else if (state == GUI_STATE_DISABLED)
Expand All @@ -827,8 +970,8 @@ RAYGUIDEF bool GuiTextBoxEx(Rectangle bounds, char *text, int textSize, bool edi
}

// Finally draw the text and selection
DrawTextBoxedSelectable(guiFont, &text[textStartIndex], textRec, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), false, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha), selStart, selLength, GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_FG)), GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_BG)));

DrawTextRecEx(guiFont, &text[textStartIndex], textRec, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), false, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha), selStart, selLength, GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_FG)), GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_BG)));
return pressed;
}

Expand Down Expand Up @@ -904,17 +1047,17 @@ static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, int *p

if (letter != '\n')
{
glyphWidth = (font.chars[index].advanceX == 0)?
glyphWidth = (font.glyphs[index].advanceX == 0)?
(int)(font.recs[index].width*scaleFactor + spacing):
(int)(font.chars[index].advanceX*scaleFactor + spacing);
(int)(font.glyphs[index].advanceX*scaleFactor + spacing);

if ((textOffsetX + glyphWidth + 1) >= rec.width) break;

if ((mode == GUI_MEASURE_MODE_CURSOR_POS) && (*pos == k)) break;
else if (mode == GUI_MEASURE_MODE_CURSOR_COORDS)
{
// Check if the mouse pointer is inside the glyph rect
Rectangle grec = {rec.x + textOffsetX - 1, rec.y, glyphWidth, (font.baseSize + font.baseSize/2)*scaleFactor - 1 };
Rectangle grec = {rec.x + textOffsetX - 1, rec.y, (float)glyphWidth, (font.baseSize + font.baseSize/2)*scaleFactor - 1 };
Vector2 mouse = GetMousePosition();

if (CheckCollisionPointRec(mouse, grec))
Expand Down Expand Up @@ -970,9 +1113,9 @@ static int GuiMeasureTextBoxRev(const char *text, int length, Rectangle rec, int

if (letter != '\n')
{
glyphWidth = (font.chars[index].advanceX == 0)?
glyphWidth = (font.glyphs[index].advanceX == 0)?
(int)(font.recs[index].width*scaleFactor + spacing):
(int)(font.chars[index].advanceX*scaleFactor + spacing);
(int)(font.glyphs[index].advanceX*scaleFactor + spacing);

if ((textOffsetX + glyphWidth + 1) >= rec.width) break;
}
Expand Down

0 comments on commit e81fd89

Please sign in to comment.