24 Commits
3.5 ... 3.6

Author SHA1 Message Date
Ray
aa81c167f1 Review description for raygui 3.6 release! 2023-05-10 00:12:20 +02:00
Ray
37992af28a REVIEWED: Support slider movement out-of-bounds 2023-05-09 21:01:03 +02:00
9cf37c5e51 Fix for sliders (#282)
A fix for sliders not dragging outside of bounds.
2023-05-09 20:54:47 +02:00
Ray
60e216283d Update raygui.h 2023-05-09 20:09:52 +02:00
7f46aa5929 GuiTextBox improvements (#281)
Adds support for HOME, END, and DELETE keys to GuiTextBox.
2023-05-09 16:51:06 +02:00
Ray
b54733ec12 Minor tweaks 2023-05-08 18:57:19 +02:00
Ray
7da92d224d Update custom_sliders.c 2023-05-08 18:48:14 +02:00
Ray
1c7ceb6fda Fixed #277 2023-05-08 18:43:22 +02:00
Ray
251f7fde3a Update README.md 2023-05-08 18:41:04 +02:00
80e802b18d Fix for #277 (#280)
Fixes incorrect comment from issue #277
2023-05-07 21:28:36 +02:00
6fc9337cd8 Update raygui.h (#279)
fix for #278
2023-05-07 20:25:41 +02:00
Ray
38a3d100e1 ADDED: GuiLoadStyleFromMemory() (binary only)
REVIEWED: `GetCodepointNext()`
2023-05-07 13:17:20 +02:00
Ray
628f2e0290 Remove trailing spaces 2023-05-01 14:03:06 +02:00
ab209bc5d1 Fixed C++ compilation in C literal in call to SetShapesTexture (#276)
* Fixed C++ compilation

* Use RAYGUI_CLITERAL
2023-04-29 13:14:40 +02:00
Ray
8c14e61214 Update raygui.h 2023-04-25 15:16:08 +02:00
Ray
27caba8834 Update raygui.h 2023-04-24 09:44:54 +02:00
Ray
31b097ee3e Update raygui.h 2023-04-23 23:54:33 +02:00
b0d7073551 Fix #274 - Adding semicolon (#275) 2023-04-23 15:34:35 +02:00
Ray
731bae72d5 REVIEWED: Some old TODOs 2023-04-22 21:17:08 +02:00
Ray
4ad311bd6f ADDED: Icon SAND_TIMER 2023-04-22 18:08:34 +02:00
Ray
c4d71e1c0b Update raygui.h 2023-04-22 18:01:18 +02:00
Ray
d04c68b915 Update raygui.h 2023-04-22 10:39:18 +02:00
Ray
42aaec6640 REVIEWED: GuiLabelButton(), avoid text cut
ADDED: Debug text rectangles
2023-04-21 09:36:22 +02:00
d05586ef0f Avoid using hardcoded values in whitechar (#273)
Proposed change avoids hardcoded values and creates white rectangle exaclty like in rcore.
2023-04-20 16:52:14 +02:00
4 changed files with 410 additions and 228 deletions

View File

@ -28,12 +28,12 @@
### basic controls ### basic controls
``` ```
Label | Button | LabelButton | Toggle | ToggleGroup | CheckBox Label | Button | LabelButton | Toggle | ToggleGroup | CheckBox
ComboBox | DropdownBox | TextBox | TextBoxMulti | ValueBox | Spinner ComboBox | DropdownBox | TextBox | ValueBox | Spinner
Slider | SliderBar | ProgressBar | StatusBar | DummyRec | Grid Slider | SliderBar | ProgressBar | StatusBar | DummyRec | Grid
``` ```
### container/separator controls ### container/separator controls
``` ```
WindowBox | GroupBox | Line | Panel | ScrollPanel WindowBox | GroupBox | Line | Panel | ScrollPanel | TabBar
``` ```
### advanced controls ### advanced controls
``` ```

View File

@ -29,11 +29,11 @@ float GuiVerticalSliderPro(Rectangle bounds, const char *textTop, const char *te
bool GuiSliderOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode); bool GuiSliderOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode);
bool GuiSliderBarOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode); bool GuiSliderBarOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, bool editMode);
float GuiSliderProOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth, bool editMode); bool GuiSliderProOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth, bool editMode);
bool GuiVerticalSliderOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode); bool GuiVerticalSliderOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode);
bool GuiVerticalSliderBarOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode); bool GuiVerticalSliderBarOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, bool editMode);
float GuiVerticalSliderProOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, int sliderHeight, bool editMode); bool GuiVerticalSliderProOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, int sliderHeight, bool editMode);
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Program main entry point // Program main entry point
@ -215,7 +215,7 @@ float GuiVerticalSliderBar(Rectangle bounds, const char *textTop, const char *te
return GuiVerticalSliderPro(bounds, textTop, textBottom, value, minValue, maxValue, 0); return GuiVerticalSliderPro(bounds, textTop, textBottom, value, minValue, maxValue, 0);
} }
float GuiSliderProOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth, bool editMode) bool GuiSliderProOwning(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth, bool editMode)
{ {
GuiState state = (GuiState)GuiGetState(); GuiState state = (GuiState)GuiGetState();
@ -338,7 +338,7 @@ bool GuiSliderBarOwning(Rectangle bounds, const char *textLeft, const char *text
return GuiSliderProOwning(bounds, textLeft, textRight, value, minValue, maxValue, 0, editMode); return GuiSliderProOwning(bounds, textLeft, textRight, value, minValue, maxValue, 0, editMode);
} }
float GuiVerticalSliderProOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, int sliderHeight, bool editMode) bool GuiVerticalSliderProOwning(Rectangle bounds, const char *textTop, const char *textBottom, float *value, float minValue, float maxValue, int sliderHeight, bool editMode)
{ {
GuiState state = (GuiState)GuiGetState(); GuiState state = (GuiState)GuiGetState();

View File

@ -1,6 +1,6 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raygui v3.5 - A simple and easy-to-use immediate-mode gui library * raygui v3.6 - A simple and easy-to-use immediate-mode gui library
* *
* DESCRIPTION: * DESCRIPTION:
* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also * raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
@ -15,10 +15,12 @@
* - Multiple tools provided for raygui development * - Multiple tools provided for raygui development
* *
* POSSIBLE IMPROVEMENTS: * POSSIBLE IMPROVEMENTS:
* - Allow some controls to work in exclusive mode: GuiSlider(), GuiScrollBar() * - Redesign functions that require a value as parameter to be returned, pass by reference
* - Better standalone mode API for easy plug of custom backends * - Better standalone mode API for easy plug of custom backends
* - Externalize required inputs in some way, allow user customization
* *
* LIMITATIONS: * LIMITATIONS:
* - No multi-line word-wraped text box support
* - No auto-layout mechanism provided, up to the user to define controls position and size * - No auto-layout mechanism provided, up to the user to define controls position and size
* - Standalone mode requires library modification and some user work to plug another backend * - Standalone mode requires library modification and some user work to plug another backend
* *
@ -124,8 +126,20 @@
* Includes custom ricons.h header defining a set of custom icons, * Includes custom ricons.h header defining a set of custom icons,
* this file can be generated using rGuiIcons tool * this file can be generated using rGuiIcons tool
* *
* #define RAYGUI_DEBUG_TEXT_BOUNDS
* Draw text bounds rectangles for debug
* *
* VERSIONS HISTORY: * VERSIONS HISTORY:
* 3.6 (10-May-2023) ADDED: New icon: SAND_TIMER
* ADDED: GuiLoadStyleFromMemory() (binary only)
* REVIEWED: GuiScrollBar() horizontal movement key
* REVIEWED: GuiTextBox() crash on cursor movement
* REVIEWED: GuiTextBox(), additional inputs support
* REVIEWED: GuiLabelButton(), avoid text cut
* REVIEWED: GuiTextInputBox(), password input
* REVIEWED: Local GetCodepointNext(), aligned with raylib
* REDESIGNED: GuiSlider*()/GuiScrollBar() to support out-of-bounds
*
* 3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle() * 3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle()
* ADDED: Helper functions to split text in separate lines * ADDED: Helper functions to split text in separate lines
* ADDED: Multiple new icons, useful for code editing tools * ADDED: Multiple new icons, useful for code editing tools
@ -137,6 +151,7 @@
* REVIEWED: Library header info, more info, better organized * REVIEWED: Library header info, more info, better organized
* REDESIGNED: GuiTextBox() to support cursor movement * REDESIGNED: GuiTextBox() to support cursor movement
* REDESIGNED: GuiDrawText() to divide drawing by lines * REDESIGNED: GuiDrawText() to divide drawing by lines
*
* 3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes * 3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes
* REMOVED: GuiScrollBar(), only internal * REMOVED: GuiScrollBar(), only internal
* REDESIGNED: GuiPanel() to support text parameter * REDESIGNED: GuiPanel() to support text parameter
@ -146,6 +161,7 @@
* REDESIGNED: GuiColorBarAlpha() to support text parameter * REDESIGNED: GuiColorBarAlpha() to support text parameter
* REDESIGNED: GuiColorBarHue() to support text parameter * REDESIGNED: GuiColorBarHue() to support text parameter
* REDESIGNED: GuiTextInputBox() to support password * REDESIGNED: GuiTextInputBox() to support password
*
* 3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool) * 3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool)
* REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures * REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures
* REVIEWED: External icons usage logic * REVIEWED: External icons usage logic
@ -153,10 +169,12 @@
* RENAMED: Multiple controls properties definitions to prepend RAYGUI_ * RENAMED: Multiple controls properties definitions to prepend RAYGUI_
* RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency * RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency
* Projects updated and multiple tweaks * Projects updated and multiple tweaks
*
* 3.0 (04-Nov-2021) Integrated ricons data to avoid external file * 3.0 (04-Nov-2021) Integrated ricons data to avoid external file
* REDESIGNED: GuiTextBoxMulti() * REDESIGNED: GuiTextBoxMulti()
* REMOVED: GuiImageButton*() * REMOVED: GuiImageButton*()
* Multiple minor tweaks and bugs corrected * Multiple minor tweaks and bugs corrected
*
* 2.9 (17-Mar-2021) REMOVED: Tooltip API * 2.9 (17-Mar-2021) REMOVED: Tooltip API
* 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle() * 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle()
* 2.7 (20-Feb-2020) ADDED: Possible tooltips API * 2.7 (20-Feb-2020) ADDED: Possible tooltips API
@ -166,6 +184,7 @@
* Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties * Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties
* ADDED: 8 new custom styles ready to use * ADDED: 8 new custom styles ready to use
* Multiple minor tweaks and bugs corrected * Multiple minor tweaks and bugs corrected
*
* 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner() * 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner()
* 2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed * 2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed
* Refactor all controls drawing mechanism to use control state * Refactor all controls drawing mechanism to use control state
@ -227,9 +246,9 @@
#define RAYGUI_H #define RAYGUI_H
#define RAYGUI_VERSION_MAJOR 3 #define RAYGUI_VERSION_MAJOR 3
#define RAYGUI_VERSION_MINOR 5 #define RAYGUI_VERSION_MINOR 6
#define RAYGUI_VERSION_PATCH 0 #define RAYGUI_VERSION_PATCH 0
#define RAYGUI_VERSION "3.5" #define RAYGUI_VERSION "3.6"
#if !defined(RAYGUI_STANDALONE) #if !defined(RAYGUI_STANDALONE)
#include "raylib.h" #include "raylib.h"
@ -826,7 +845,7 @@ typedef enum {
ICON_REG_EXP = 216, ICON_REG_EXP = 216,
ICON_FOLDER = 217, ICON_FOLDER = 217,
ICON_FILE = 218, ICON_FILE = 218,
ICON_219 = 219, ICON_SAND_TIMER = 219,
ICON_220 = 220, ICON_220 = 220,
ICON_221 = 221, ICON_221 = 221,
ICON_222 = 222, ICON_222 = 222,
@ -894,6 +913,11 @@ typedef enum {
#define RAYGUI_CLITERAL(name) (name) #define RAYGUI_CLITERAL(name) (name)
#endif #endif
// Check if two rectangles are equal, used to validate a slider bounds as an id
#ifndef CHECK_BOUNDS_ID
#define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
#endif
#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS) #if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
// Embedded icons, no external file provided // Embedded icons, no external file provided
@ -1139,7 +1163,7 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] =
0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000, // ICON_REG_EXP 0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000, // ICON_REG_EXP
0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER
0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_219 0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0, // ICON_SAND_TIMER
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222
@ -1211,7 +1235,10 @@ static unsigned int guiIconScale = 1; // Gui icon default scale (if ic
static bool guiTooltip = false; // Tooltip enabled/disabled static bool guiTooltip = false; // Tooltip enabled/disabled
static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user) static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user)
static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() static bool guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider)
static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier
static unsigned int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*()
//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking //static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking
static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down
static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement
@ -1300,6 +1327,8 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only)
static int GetTextWidth(const char *text); // Gui get text width using gui font and style static int GetTextWidth(const char *text); // Gui get text width using gui font and style
static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds
static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor
@ -1483,7 +1512,7 @@ void GuiLine(Rectangle bounds, const char *text)
else else
{ {
Rectangle textBounds = { 0 }; Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(text); textBounds.width = (float)GetTextWidth(text) + 2;
textBounds.height = bounds.height; textBounds.height = bounds.height;
textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT; textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
textBounds.y = bounds.y; textBounds.y = bounds.y;
@ -1656,8 +1685,8 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content,
#endif #endif
float wheelMove = GetMouseWheelMove(); float wheelMove = GetMouseWheelMove();
// Horizontal scroll (Shift + Mouse wheel) // Horizontal scroll ((Left Control or Left Shift) + Mouse wheel)
if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_SHIFT))) scrollPos.x += wheelMove*20; if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*20;
else scrollPos.y += wheelMove*20; // Vertical scroll else scrollPos.y += wheelMove*20; // Vertical scroll
} }
} }
@ -1739,7 +1768,7 @@ bool GuiButton(Rectangle bounds, const char *text)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -1773,11 +1802,11 @@ bool GuiLabelButton(Rectangle bounds, const char *text)
// NOTE: We force bounds.width to be all text // NOTE: We force bounds.width to be all text
float textWidth = (float)GetTextWidth(text); float textWidth = (float)GetTextWidth(text);
if (bounds.width < textWidth) bounds.width = textWidth; if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -1807,7 +1836,7 @@ bool GuiToggle(Rectangle bounds, const char *text, bool active)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -1887,7 +1916,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked)
if (text != NULL) if (text != NULL)
{ {
textBounds.width = (float)GetTextWidth(text); textBounds.width = (float)GetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING); textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@ -1896,7 +1925,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -1956,7 +1985,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int active)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1)) if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2018,7 +2047,7 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1)) if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2118,7 +2147,7 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo
bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
{ {
#define AUTO_CURSOR_COOLDOWN 40 // Frames to wait for autocursor movement #define AUTO_CURSOR_COOLDOWN 40 // Frames to wait for autocursor movement
#define AUTO_CURSOR_DELAY 3 // Frames delay for autocursor movement #define AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement
GuiState state = guiState; GuiState state = guiState;
bool pressed = false; bool pressed = false;
@ -2152,7 +2181,7 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
// Auto-cursor movement logic // Auto-cursor movement logic
// NOTE: Cursor moves automatically when key down after some time // NOTE: Cursor moves automatically when key down after some time
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE)) autoCursorCooldownCounter++; if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++;
else else
{ {
autoCursorCooldownCounter = 0; // GLOBAL: Cursor cooldown counter autoCursorCooldownCounter = 0; // GLOBAL: Cursor cooldown counter
@ -2165,7 +2194,7 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2189,6 +2218,8 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
int codepoint = GetCharPressed(); // Get Unicode codepoint int codepoint = GetCharPressed(); // Get Unicode codepoint
if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n';
if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
// Encode codepoint as UTF-8 // Encode codepoint as UTF-8
int codepointSize = 0; int codepointSize = 0;
const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
@ -2210,7 +2241,39 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
text[textLength] = '\0'; text[textLength] = '\0';
} }
// Delete codepoint from text, at current cursor position // Move cursor to start
if ((textLength > 0) && IsKeyPressed(KEY_HOME))
{
textBoxCursorIndex = 0;
}
// Move cursor to end
if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END))
{
textBoxCursorIndex = textLength;
}
// Delete codepoint from text, after current cursor position
if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= AUTO_CURSOR_COOLDOWN))))
{
autoCursorDelayCounter++;
if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames
{
int nextCodepointSize = 0;
GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
// Move backward text from cursor position
for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
textLength -= codepointSize;
// Make sure text last character is EOL
text[textLength] = '\0';
}
}
// Delete codepoint from text, before current cursor position
if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= AUTO_CURSOR_COOLDOWN)))) if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= AUTO_CURSOR_COOLDOWN))))
{ {
autoCursorDelayCounter++; autoCursorDelayCounter++;
@ -2223,8 +2286,12 @@ bool GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode)
// Move backward text from cursor position // Move backward text from cursor position
for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize]; for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
textBoxCursorIndex -= codepointSize; // Prevent cursor index from decrementing past 0
textLength -= codepointSize; if (textBoxCursorIndex > 0)
{
textBoxCursorIndex -= codepointSize;
textLength -= codepointSize;
}
// Make sure text last character is EOL // Make sure text last character is EOL
text[textLength] = '\0'; text[textLength] = '\0';
@ -2331,7 +2398,8 @@ bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode
GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1);
GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1); GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1);
pressed = GuiTextBox(bounds, text, bufferSize, editMode); // TODO: Implement methods to calculate cursor position properly // TODO: Implement methods to calculate cursor position properly
pressed = GuiTextBox(bounds, text, bufferSize, editMode);
GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0); GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0);
GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0);
@ -2356,7 +2424,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in
Rectangle textBounds = { 0 }; Rectangle textBounds = { 0 };
if (text != NULL) if (text != NULL)
{ {
textBounds.width = (float)GetTextWidth(text); textBounds.width = (float)GetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING); textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@ -2365,7 +2433,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2394,7 +2462,6 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in
// Draw control // Draw control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// TODO: Set Spinner properties for ValueBox
pressed = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode); pressed = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode);
// Draw value selector custom buttons // Draw value selector custom buttons
@ -2432,7 +2499,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i
Rectangle textBounds = { 0 }; Rectangle textBounds = { 0 };
if (text != NULL) if (text != NULL)
{ {
textBounds.width = (float)GetTextWidth(text); textBounds.width = (float)GetTextWidth(text) + 2;
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@ -2441,7 +2508,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2554,11 +2621,29 @@ float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
if (CheckCollisionPointRec(mousePoint, bounds)) if (guiSliderDragging) // Keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
{
// Get equivalent value and slider position from mousePoint.x
value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
}
}
else
{
guiSliderDragging = false;
guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds))
{ {
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{ {
state = STATE_PRESSED; state = STATE_PRESSED;
guiSliderDragging = true;
guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
// Get equivalent value and slider position from mousePoint.x // Get equivalent value and slider position from mousePoint.x
value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
@ -2703,7 +2788,7 @@ void GuiDummyRec(Rectangle bounds, const char *text)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2763,7 +2848,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, in
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2901,7 +2986,7 @@ Color GuiColorPanel(Rectangle bounds, const char *text, Color color)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
@ -2974,12 +3059,30 @@ float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
if (CheckCollisionPointRec(mousePoint, bounds) || if (guiSliderDragging) // Keep dragging outside of bounds
CheckCollisionPointRec(mousePoint, selector)) {
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
{
alpha = (mousePoint.x - bounds.x)/bounds.width;
if (alpha <= 0.0f) alpha = 0.0f;
if (alpha >= 1.0f) alpha = 1.0f;
}
}
else
{
guiSliderDragging = false;
guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
{ {
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{ {
state = STATE_PRESSED; state = STATE_PRESSED;
guiSliderDragging = true;
guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
alpha = (mousePoint.x - bounds.x)/bounds.width; alpha = (mousePoint.x - bounds.x)/bounds.width;
if (alpha <= 0.0f) alpha = 0.0f; if (alpha <= 0.0f) alpha = 0.0f;
@ -3039,12 +3142,30 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
if (CheckCollisionPointRec(mousePoint, bounds) || if (guiSliderDragging) // Keep dragging outside of bounds
CheckCollisionPointRec(mousePoint, selector)) {
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
{
hue = (mousePoint.y - bounds.y)*360/bounds.height;
if (hue <= 0.0f) hue = 0.0f;
if (hue >= 359.0f) hue = 359.0f;
}
}
else
{
guiSliderDragging = false;
guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
{ {
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{ {
state = STATE_PRESSED; state = STATE_PRESSED;
guiSliderDragging = true;
guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
hue = (mousePoint.y - bounds.y)*360/bounds.height; hue = (mousePoint.y - bounds.y)*360/bounds.height;
if (hue <= 0.0f) hue = 0.0f; if (hue <= 0.0f) hue = 0.0f;
@ -3197,7 +3318,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co
Rectangle textBounds = { 0 }; Rectangle textBounds = { 0 };
if (message != NULL) if (message != NULL)
{ {
int textSize = GetTextWidth(message); int textSize = GetTextWidth(message) + 2;
textBounds.x = bounds.x + bounds.width/2 - textSize/2; textBounds.x = bounds.x + bounds.width/2 - textSize/2;
textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2; textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@ -3248,6 +3369,8 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co
buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
} }
if (btnIndex >= 0) textEditMode = false;
GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment);
//-------------------------------------------------------------------- //--------------------------------------------------------------------
@ -3274,7 +3397,7 @@ Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs)
// Update control // Update control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
if ((state != STATE_DISABLED) && !guiLocked) if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging)
{ {
if (CheckCollisionPointRec(mousePoint, bounds)) if (CheckCollisionPointRec(mousePoint, bounds))
{ {
@ -3287,9 +3410,6 @@ Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs)
// Draw control // Draw control
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// TODO: Draw background panel?
switch (state) switch (state)
{ {
case STATE_NORMAL: case STATE_NORMAL:
@ -3426,125 +3546,24 @@ void GuiLoadStyle(const char *fileName)
{ {
rgsFile = fopen(fileName, "rb"); rgsFile = fopen(fileName, "rb");
if (rgsFile == NULL) return; if (rgsFile != NULL)
char signature[5] = { 0 };
short version = 0;
short reserved = 0;
int propertyCount = 0;
fread(signature, 1, 4, rgsFile);
fread(&version, sizeof(short), 1, rgsFile);
fread(&reserved, sizeof(short), 1, rgsFile);
fread(&propertyCount, sizeof(int), 1, rgsFile);
if ((signature[0] == 'r') &&
(signature[1] == 'G') &&
(signature[2] == 'S') &&
(signature[3] == ' '))
{ {
short controlId = 0; fseek(rgsFile, 0, SEEK_END);
short propertyId = 0; int fileDataSize = ftell(rgsFile);
unsigned int propertyValue = 0; fseek(rgsFile, 0, SEEK_SET);
for (int i = 0; i < propertyCount; i++) if (fileDataSize > 0)
{ {
fread(&controlId, sizeof(short), 1, rgsFile); unsigned char *fileData = (unsigned char *)RL_MALLOC(fileDataSize*sizeof(unsigned char));
fread(&propertyId, sizeof(short), 1, rgsFile); fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
fread(&propertyValue, sizeof(unsigned int), 1, rgsFile);
if (controlId == 0) // DEFAULT control GuiLoadStyleFromMemory(fileData, fileDataSize);
{
// If a DEFAULT property is loaded, it is propagated to all controls
// NOTE: All DEFAULT properties should be defined first in the file
GuiSetStyle(0, (int)propertyId, propertyValue);
if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue); RL_FREE(fileData);
}
else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
} }
// Font loading is highly dependant on raylib API to load font data and image fclose(rgsFile);
#if !defined(RAYGUI_STANDALONE)
// Load custom font if available
int fontDataSize = 0;
fread(&fontDataSize, sizeof(int), 1, rgsFile);
if (fontDataSize > 0)
{
Font font = { 0 };
int fontType = 0; // 0-Normal, 1-SDF
Rectangle whiteRec = { 0 };
fread(&font.baseSize, sizeof(int), 1, rgsFile);
fread(&font.glyphCount, sizeof(int), 1, rgsFile);
fread(&fontType, sizeof(int), 1, rgsFile);
// Load font white rectangle
fread(&whiteRec, sizeof(Rectangle), 1, rgsFile);
// Load font image parameters
int fontImageUncompSize = 0;
int fontImageCompSize = 0;
fread(&fontImageUncompSize, sizeof(int), 1, rgsFile);
fread(&fontImageCompSize, sizeof(int), 1, rgsFile);
Image imFont = { 0 };
imFont.mipmaps = 1;
fread(&imFont.width, sizeof(int), 1, rgsFile);
fread(&imFont.height, sizeof(int), 1, rgsFile);
fread(&imFont.format, sizeof(int), 1, rgsFile);
if (fontImageCompSize < fontImageUncompSize)
{
// Compressed font atlas image data (DEFLATE), it requires DecompressData()
int dataUncompSize = 0;
unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
fread(compData, 1, fontImageCompSize, rgsFile);
imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize);
// Security check, dataUncompSize must match the provided fontImageUncompSize
if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted");
RAYGUI_FREE(compData);
}
else
{
// Font atlas image data is not compressed
imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
fread(imFont.data, 1, fontImageUncompSize, rgsFile);
}
if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
font.texture = LoadTextureFromImage(imFont);
if (font.texture.id == 0) font = GetFontDefault();
RAYGUI_FREE(imFont.data);
// Load font recs data
font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle));
for (int i = 0; i < font.glyphCount; i++) fread(&font.recs[i], sizeof(Rectangle), 1, rgsFile);
// Load font chars info data
font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo));
for (int i = 0; i < font.glyphCount; i++)
{
fread(&font.glyphs[i].value, sizeof(int), 1, rgsFile);
fread(&font.glyphs[i].offsetX, sizeof(int), 1, rgsFile);
fread(&font.glyphs[i].offsetY, sizeof(int), 1, rgsFile);
fread(&font.glyphs[i].advanceX, sizeof(int), 1, rgsFile);
}
GuiSetFont(font);
// Set font texture source rectangle to be used as white texture to draw shapes
// NOTE: This way, all gui can be draw using a single draw call
if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec);
}
#endif
} }
fclose(rgsFile);
} }
} }
@ -3603,7 +3622,7 @@ void GuiLoadStyleDefault(void)
GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, 4); GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, (int)((float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f));
GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4);
GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24); GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24);
GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2); GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2);
@ -3632,9 +3651,10 @@ void GuiLoadStyleDefault(void)
// Setup default raylib font // Setup default raylib font
guiFont = GetFontDefault(); guiFont = GetFontDefault();
// Setup default raylib font rectangle // NOTE: Default raylib font character 95 is a white square
Rectangle whiteChar = { 41, 46, 2, 8 }; Rectangle whiteChar = guiFont.recs[95];
SetShapesTexture(guiFont.texture, whiteChar); // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 });
} }
} }
@ -3781,6 +3801,145 @@ void GuiSetIconScale(int scale)
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Definition // Module specific Functions Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Load style from memory (binary only)
static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
{
unsigned char *fileDataPtr = (unsigned char *)fileData;
char signature[5] = { 0 };
short version = 0;
short reserved = 0;
int propertyCount = 0;
memcpy(signature, fileDataPtr, 4);
memcpy(&version, fileDataPtr + 4, sizeof(short));
memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
memcpy(&propertyCount, fileDataPtr + 4 + 2 + 2, sizeof(int));
fileDataPtr += 12;
if ((signature[0] == 'r') &&
(signature[1] == 'G') &&
(signature[2] == 'S') &&
(signature[3] == ' '))
{
short controlId = 0;
short propertyId = 0;
unsigned int propertyValue = 0;
for (int i = 0; i < propertyCount; i++)
{
memcpy(&controlId, fileDataPtr, sizeof(short));
memcpy(&propertyId, fileDataPtr + 2, sizeof(short));
memcpy(&propertyValue, fileDataPtr + 2 + 2, sizeof(unsigned int));
fileDataPtr += 8;
if (controlId == 0) // DEFAULT control
{
// If a DEFAULT property is loaded, it is propagated to all controls
// NOTE: All DEFAULT properties should be defined first in the file
GuiSetStyle(0, (int)propertyId, propertyValue);
if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue);
}
else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
}
// Font loading is highly dependant on raylib API to load font data and image
#if !defined(RAYGUI_STANDALONE)
// Load custom font if available
int fontDataSize = 0;
memcpy(&fontDataSize, fileDataPtr, sizeof(int));
fileDataPtr += 4;
if (fontDataSize > 0)
{
Font font = { 0 };
int fontType = 0; // 0-Normal, 1-SDF
Rectangle whiteRec = { 0 };
memcpy(&font.baseSize, fileDataPtr, sizeof(int));
memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int));
memcpy(&fontType, fileDataPtr + 4 + 4, sizeof(int));
fileDataPtr += 12;
// Load font white rectangle
memcpy(&whiteRec, fileDataPtr, sizeof(Rectangle));
fileDataPtr += 16;
// Load font image parameters
int fontImageUncompSize = 0;
int fontImageCompSize = 0;
memcpy(&fontImageUncompSize, fileDataPtr, sizeof(int));
memcpy(&fontImageCompSize, fileDataPtr + 4, sizeof(int));
fileDataPtr += 8;
Image imFont = { 0 };
imFont.mipmaps = 1;
memcpy(&imFont.width, fileDataPtr, sizeof(int));
memcpy(&imFont.height, fileDataPtr + 4, sizeof(int));
memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int));
fileDataPtr += 12;
if (fontImageCompSize < fontImageUncompSize)
{
// Compressed font atlas image data (DEFLATE), it requires DecompressData()
int dataUncompSize = 0;
unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
memcpy(compData, fileDataPtr, fontImageCompSize);
fileDataPtr += fontImageCompSize;
imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize);
// Security check, dataUncompSize must match the provided fontImageUncompSize
if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted");
RAYGUI_FREE(compData);
}
else
{
// Font atlas image data is not compressed
imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
fileDataPtr += fontImageUncompSize;
}
if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
font.texture = LoadTextureFromImage(imFont);
if (font.texture.id == 0) font = GetFontDefault();
RAYGUI_FREE(imFont.data);
// Load font recs data
font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle));
for (int i = 0; i < font.glyphCount; i++)
{
memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle));
fileDataPtr += sizeof(Rectangle);
}
// Load font chars info data
font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo));
for (int i = 0; i < font.glyphCount; i++)
{
memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int));
memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int));
memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int));
memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int));
fileDataPtr += 16;
}
GuiSetFont(font);
// Set font texture source rectangle to be used as white texture to draw shapes
// NOTE: This way, all gui can be draw using a single draw call
if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec);
}
#endif
}
}
// Gui get text width considering icon // Gui get text width considering icon
static int GetTextWidth(const char *text) static int GetTextWidth(const char *text)
{ {
@ -3795,7 +3954,7 @@ static int GetTextWidth(const char *text)
{ {
if (text[0] == '#') if (text[0] == '#')
{ {
for (int i = 1; (text[i] != '\0') && (i < 5); i++) for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
{ {
if (text[i] == '#') if (text[i] == '#')
{ {
@ -3854,10 +4013,10 @@ static Rectangle GetTextBounds(int control, Rectangle bounds)
textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);
// Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT
// TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?)
// More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER
switch (control) switch (control)
{ {
//case TEXTBOX: break; // TODO: Consider multi-line text?
//case VALUEBOX: break; // NOTE: ValueBox text value always centered, text padding applies to label
default: default:
{ {
if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING);
@ -3866,9 +4025,6 @@ static Rectangle GetTextBounds(int control, Rectangle bounds)
break; break;
} }
// TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?)
// More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER
return textBounds; return textBounds;
} }
@ -3969,7 +4125,7 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
Vector2 position = { bounds.x, bounds.y }; Vector2 position = { bounds.x, bounds.y };
// TODO: We get text size after icon has been processed // NOTE: We get text size after icon has been processed
// WARNING: GetTextWidth() also processes text icon to get width! -> Really needed? // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
int textSizeX = GetTextWidth(lines[i]); int textSizeX = GetTextWidth(lines[i]);
@ -4051,10 +4207,14 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color
} }
} }
posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; // TODO: GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)? // TODO: Allow users to set line spacing for text: GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, x)
posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f;
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
} }
} }
#if defined(RAYGUI_DEBUG_TEXT_BOUNDS)
GuiDrawRectangle(bounds, 0, WHITE, Fade(RED, 0.4f));
#endif
} }
// Gui draw rectangle using default raygui plain style with borders // Gui draw rectangle using default raygui plain style with borders
@ -4079,7 +4239,7 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor,
// Draw tooltip using control bounds // Draw tooltip using control bounds
static void GuiTooltip(Rectangle controlRec) static void GuiTooltip(Rectangle controlRec)
{ {
if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL)) if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiSliderDragging)
{ {
Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)); Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING));
@ -4108,7 +4268,7 @@ static const char **GuiTextSplit(const char *text, char delimiter, int *count, i
// 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
// NOTE: Those definitions could be externally provided if required // NOTE: Those definitions could be externally provided if required
// WARNING: HACK: TODO: Review! // TODO: HACK: GuiTextSplit() - Review how textRows are returned to user
// textRow is an externally provided array of integers that stores row number for every splitted string // textRow is an externally provided array of integers that stores row number for every splitted string
#if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
@ -4347,9 +4507,27 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
{ {
Vector2 mousePoint = GetMousePosition(); Vector2 mousePoint = GetMousePosition();
if (CheckCollisionPointRec(mousePoint, bounds)) if (guiSliderDragging) // Keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
{
if (isVertical) value += (GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange);
else value += (GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange);
}
}
else
{
guiSliderDragging = false;
guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
}
}
else if (CheckCollisionPointRec(mousePoint, bounds))
{ {
state = STATE_FOCUSED; state = STATE_FOCUSED;
guiSliderDragging = true;
guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
// Handle mouse wheel // Handle mouse wheel
int wheel = (int)GetMouseWheelMove(); int wheel = (int)GetMouseWheelMove();
@ -4607,34 +4785,38 @@ static int GetCodepointNext(const char *text, int *codepointSize)
{ {
const char *ptr = text; const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?') int codepoint = 0x3f; // Codepoint (defaults to '?')
*codepointSize = 0; *codepointSize = 1;
// Get current codepoint and bytes processed // Get current codepoint and bytes processed
if (0xf0 == (0xf8 & ptr[0])) if (0xf0 == (0xf8 & ptr[0]))
{ {
// 4 byte UTF-8 codepoint // 4 byte UTF-8 codepoint
if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
*codepointSize = 4; *codepointSize = 4;
} }
else if (0xe0 == (0xf0 & ptr[0])) else if (0xe0 == (0xf0 & ptr[0]))
{ {
// 3 byte UTF-8 codepoint */ // 3 byte UTF-8 codepoint */
if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]); codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3; *codepointSize = 3;
} }
else if (0xc0 == (0xe0 & ptr[0])) else if (0xc0 == (0xe0 & ptr[0]))
{ {
// 2 byte UTF-8 codepoint // 2 byte UTF-8 codepoint
if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
*codepointSize = 2; *codepointSize = 2;
} }
else else if (0x00 == (0x80 & ptr[0]))
{ {
// 1 byte UTF-8 codepoint // 1 byte UTF-8 codepoint
codepoint = ptr[0]; codepoint = ptr[0];
*codepointSize = 1; *codepointSize = 1;
} }
return codepoint; return codepoint;
} }
#endif // RAYGUI_STANDALONE #endif // RAYGUI_STANDALONE