From ee07622183a898b0004a4f5641db93c6d8a1ec55 Mon Sep 17 00:00:00 2001 From: cwj3rcb Date: Sun, 14 Sep 2025 13:21:46 -0500 Subject: [PATCH] added raygui and tried to make it not throw errors --- .../clangd/index/main.c.329D1ED40D99E8B1.idx | Bin 0 -> 13818 bytes .../index/raygui.h.B9CCA7B7A0CAA820.idx | Bin 0 -> 127212 bytes .../index/raygui_impl.c.68F311B44C225DF3.idx | Bin 0 -> 468 bytes CMakeLists.txt | 45 +- src/main.c | 1 + src/raygui.h | 5994 +++++++++++++++++ src/raygui_impl.c | 2 + 7 files changed, 6032 insertions(+), 10 deletions(-) create mode 100644 .cache/clangd/index/main.c.329D1ED40D99E8B1.idx create mode 100644 .cache/clangd/index/raygui.h.B9CCA7B7A0CAA820.idx create mode 100644 .cache/clangd/index/raygui_impl.c.68F311B44C225DF3.idx create mode 100644 src/raygui.h create mode 100644 src/raygui_impl.c diff --git a/.cache/clangd/index/main.c.329D1ED40D99E8B1.idx b/.cache/clangd/index/main.c.329D1ED40D99E8B1.idx new file mode 100644 index 0000000000000000000000000000000000000000..03a13f0004846d3fb3cfbd58ce65745922de1e8e GIT binary patch literal 13818 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

M^8m}C|x}=T|HGC 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/raygui_impl.c.68F311B44C225DF3.idx b/.cache/clangd/index/raygui_impl.c.68F311B44C225DF3.idx new file mode 100644 index 0000000000000000000000000000000000000000..e6bad44bac09882ce6b1cfbd4c60c5111be4e228 GIT binary patch literal 468 zcmWIYbaOky$iU#7;#rZKT9U}Zz`!5^#Kk2=nGrzoo6HOh6?4`Goyd- zf#0|7mXdq-uIGLb-hPjZdF#q=3)Y$hK2*II?$P)1ev9E2uXOv1>~D2qHG0IiL?2(= z%P5-9nmJMFQuYnbr{8)e?@(&XE;+uM`Aqckw-bX|6HAxZ7idiRB*FC9^l@LI)$SLn zMN9t*Bzf}__@zGM3s|1Q4ne)Rp~%G@MiFf$aTrWHfzoJ0^^T$Efa z4x|}n&R(m#(0TvD8AfRaX^;#AB/assets" COMMENT "Copying assets next to the executable" ) -# Compile-time asset root: absolute in Debug; relative otherwise +# Compile-time asset root target_compile_definitions(${PROJECT_NAME} PRIVATE $<$:ASSETS_PATH="${ASSETS_DIR}/"> $<$>:ASSETS_PATH="./assets/"> diff --git a/src/main.c b/src/main.c index 050dbb3..a63e762 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,7 @@ #include "stdint.h" #include "rlgl.h" #include "float.h" +#include "raygui.h" //---------------------------------------------------------------------------------- // Defines and Macros diff --git a/src/raygui.h b/src/raygui.h new file mode 100644 index 0000000..6999bd5 --- /dev/null +++ b/src/raygui.h @@ -0,0 +1,5994 @@ +/******************************************************************************************* +* +* raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library +* +* DESCRIPTION: +* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also +* available as a standalone library, as long as input and drawing functions are provided +* +* FEATURES: +* - Immediate-mode gui, minimal retained data +* - +25 controls provided (basic and advanced) +* - Styling system for colors, font and metrics +* - Icons supported, embedded as a 1-bit icons pack +* - Standalone mode option (custom input/graphics backend) +* - Multiple support tools provided for raygui development +* +* POSSIBLE IMPROVEMENTS: +* - Better standalone mode API for easy plug of custom backends +* - Externalize required inputs, allow user easier customization +* +* LIMITATIONS: +* - No editable multi-line word-wraped text box supported +* - No auto-layout mechanism, up to the user to define controls position and size +* - Standalone mode requires library modification and some user work to plug another backend +* +* NOTES: +* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for +* font atlas recs and glyphs, freeing that memory is (usually) up to the user, +* no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads +* by default any previously loaded font (texture, recs, glyphs) +* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions +* +* CONTROLS PROVIDED: +* # Container/separators Controls +* - WindowBox --> StatusBar, Panel +* - GroupBox --> Line +* - Line +* - Panel --> StatusBar +* - ScrollPanel --> StatusBar +* - TabBar --> Button +* +* # Basic Controls +* - Label +* - LabelButton --> Label +* - Button +* - Toggle +* - ToggleGroup --> Toggle +* - ToggleSlider +* - CheckBox +* - ComboBox +* - DropdownBox +* - TextBox +* - ValueBox --> TextBox +* - Spinner --> Button, ValueBox +* - Slider +* - SliderBar --> Slider +* - ProgressBar +* - StatusBar +* - DummyRec +* - Grid +* +* # Advance Controls +* - ListView +* - ColorPicker --> ColorPanel, ColorBarHue +* - MessageBox --> Window, Label, Button +* - TextInputBox --> Window, Label, TextBox, Button +* +* It also provides a set of functions for styling the controls based on its properties (size, color) +* +* +* RAYGUI STYLE (guiStyle): +* raygui uses a global data array for all gui style properties (allocated on data segment by default), +* when a new style is loaded, it is loaded over the global style... but a default gui style could always be +* recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one +* +* The global style array size is fixed and depends on the number of controls and properties: +* +* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)]; +* +* guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB +* +* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style +* used for all controls, when any of those base values is set, it is automatically populated to all +* controls, so, specific control values overwriting generic style should be set after base values +* +* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those +* properties are actually common to all controls and can not be overwritten individually (like BASE ones) +* Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR +* +* Custom control properties can be defined using the EXTENDED properties for each independent control. +* +* TOOL: rGuiStyler is a visual tool to customize raygui style: github.com/raysan5/rguistyler +* +* +* RAYGUI ICONS (guiIcons): +* raygui could use a global array containing icons data (allocated on data segment by default), +* a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set +* must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded +* +* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon +* requires 8 integers (16*16/32) to be stored in memory. +* +* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set +* +* The global icons array size is fixed and depends on the number of icons and size: +* +* static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS]; +* +* guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB +* +* TOOL: rGuiIcons is a visual tool to customize/create raygui icons: github.com/raysan5/rguiicons +* +* RAYGUI LAYOUT: +* raygui currently does not provide an auto-layout mechanism like other libraries, +* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it +* +* TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout +* +* CONFIGURATION: +* #define RAYGUI_IMPLEMENTATION +* Generates the implementation of the library into the included file +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation +* +* #define RAYGUI_STANDALONE +* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined +* internally in the library and input management and drawing functions must be provided by +* the user (check library implementation for further details) +* +* #define RAYGUI_NO_ICONS +* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB) +* +* #define RAYGUI_CUSTOM_ICONS +* Includes custom ricons.h header defining a set of custom icons, +* this file can be generated using rGuiIcons tool +* +* #define RAYGUI_DEBUG_RECS_BOUNDS +* Draw control bounds rectangles for debug +* +* #define RAYGUI_DEBUG_TEXT_BOUNDS +* Draw text bounds rectangles for debug +* +* VERSIONS HISTORY: +* 5.0-dev (2025) Current dev version... +* ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes +* ADDED: GuiValueBoxFloat() +* ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP +* ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH +* ADDED: GuiLoadIconsFromMemory() +* ADDED: Multiple new icons +* REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties +* REMOVED: GuiSliderPro(), functionality was redundant +* REVIEWED: Controls using text labels to use LABEL properties +* REVIEWED: Replaced sprintf() by snprintf() for more safety +* REVIEWED: GuiTabBar(), close tab with mouse middle button +* REVIEWED: GuiScrollPanel(), scroll speed proportional to content +* REVIEWED: GuiDropdownBox(), support roll up and hidden arrow +* REVIEWED: GuiTextBox(), cursor position initialization +* REVIEWED: GuiSliderPro(), control value change check +* REVIEWED: GuiGrid(), simplified implementation +* REVIEWED: GuiIconText(), increase buffer size and reviewed padding +* REVIEWED: GuiDrawText(), improved wrap mode drawing +* REVIEWED: GuiScrollBar(), minor tweaks +* REVIEWED: GuiProgressBar(), improved borders computing +* REVIEWED: GuiTextBox(), multiple improvements: autocursor and more +* REVIEWED: Functions descriptions, removed wrong return value reference +* REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion +* +* 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() +* ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() +* ADDED: Multiple new icons, mostly compiler related +* ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE +* ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode +* ADDED: Support loading styles with custom font charset from external file +* REDESIGNED: GuiTextBox(), support mouse cursor positioning +* REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only) +* REDESIGNED: GuiProgressBar() to be more visual, progress affects border color +* REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText() +* REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value +* REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value +* REDESIGNED: GuiComboBox(), get parameters by reference and return result value +* REDESIGNED: GuiCheckBox(), get parameters by reference and return result value +* REDESIGNED: GuiSlider(), get parameters by reference and return result value +* REDESIGNED: GuiSliderBar(), get parameters by reference and return result value +* REDESIGNED: GuiProgressBar(), get parameters by reference and return result value +* REDESIGNED: GuiListView(), get parameters by reference and return result value +* REDESIGNED: GuiColorPicker(), get parameters by reference and return result value +* REDESIGNED: GuiColorPanel(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarAlpha(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarHue(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), added extra parameter +* REDESIGNED: GuiListViewEx(), change parameters order +* REDESIGNED: All controls return result as int value +* REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars +* REVIEWED: All examples and specially controls_test_suite +* RENAMED: gui_file_dialog module to gui_window_file_dialog +* UPDATED: All styles to include ISO-8859-15 charset (as much as possible) +* +* 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() +* ADDED: Helper functions to split text in separate lines +* ADDED: Multiple new icons, useful for code editing tools +* REMOVED: Unneeded icon editing functions +* REMOVED: GuiTextBoxMulti(), very limited and broken +* REMOVED: MeasureTextEx() dependency, logic directly implemented +* REMOVED: DrawTextEx() dependency, logic directly implemented +* REVIEWED: GuiScrollBar(), improve mouse-click behaviour +* REVIEWED: Library header info, more info, better organized +* REDESIGNED: GuiTextBox() to support cursor movement +* REDESIGNED: GuiDrawText() to divide drawing by lines +* +* 3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes +* REMOVED: GuiScrollBar(), only internal +* REDESIGNED: GuiPanel() to support text parameter +* REDESIGNED: GuiScrollPanel() to support text parameter +* REDESIGNED: GuiColorPicker() to support text parameter +* REDESIGNED: GuiColorPanel() to support text parameter +* REDESIGNED: GuiColorBarAlpha() to support text parameter +* REDESIGNED: GuiColorBarHue() to support text parameter +* REDESIGNED: GuiTextInputBox() to support password +* +* 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: External icons usage logic +* REVIEWED: GuiLine() for centered alignment when including text +* RENAMED: Multiple controls properties definitions to prepend RAYGUI_ +* RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency +* Projects updated and multiple tweaks +* +* 3.0 (04-Nov-2021) Integrated ricons data to avoid external file +* REDESIGNED: GuiTextBoxMulti() +* REMOVED: GuiImageButton*() +* Multiple minor tweaks and bugs corrected +* +* 2.9 (17-Mar-2021) REMOVED: Tooltip API +* 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle() +* 2.7 (20-Feb-2020) ADDED: Possible tooltips API +* 2.6 (09-Sep-2019) ADDED: GuiTextInputBox() +* REDESIGNED: GuiListView*(), GuiDropdownBox(), GuiSlider*(), GuiProgressBar(), GuiMessageBox() +* REVIEWED: GuiTextBox(), GuiSpinner(), GuiValueBox(), GuiLoadStyle() +* Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties +* ADDED: 8 new custom styles ready to use +* Multiple minor tweaks and bugs corrected +* +* 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 +* Refactor all controls drawing mechanism to use control state +* 2.2 (05-Feb-2019) ADDED: GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls +* 2.1 (26-Dec-2018) REDESIGNED: GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string +* REDESIGNED: Style system (breaking change) +* 2.0 (08-Nov-2018) ADDED: Support controls guiLock and custom fonts +* REVIEWED: GuiComboBox(), GuiListView()... +* 1.9 (09-Oct-2018) REVIEWED: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()... +* 1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout +* 1.5 (21-Jun-2017) Working in an improved styles system +* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones) +* 1.3 (12-Jun-2017) Complete redesign of style system +* 1.1 (01-Jun-2017) Complete review of the library +* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria +* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria +* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria +* +* DEPENDENCIES: +* raylib 5.0 - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* +* STANDALONE MODE: +* By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled +* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs +* +* The following functions should be redefined for a custom backend: +* +* - Vector2 GetMousePosition(void); +* - float GetMouseWheelMove(void); +* - bool IsMouseButtonDown(int button); +* - bool IsMouseButtonPressed(int button); +* - bool IsMouseButtonReleased(int button); +* - bool IsKeyDown(int key); +* - bool IsKeyPressed(int key); +* - int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +* +* - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +* - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +* +* - Font GetFontDefault(void); // -- GuiLoadStyleDefault() +* - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle() +* - Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +* - void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) +* - char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +* - void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data +* - const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs +* - int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +* - void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list +* - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +* +* CONTRIBUTORS: +* Ramon Santamaria: Supervision, review, redesign, update and maintenance +* Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019) +* Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018) +* Adria Arranz: Testing and implementation of additional controls (2018) +* Jordi Jorba: Testing and implementation of additional controls (2018) +* Albert Martos: Review and testing of the library (2015) +* Ian Eito: Review and testing of the library (2015) +* Kevin Gato: Initial implementation of basic components (2014) +* Daniel Nicolas: Initial implementation of basic components (2014) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYGUI_H +#define RAYGUI_H + +#define RAYGUI_VERSION_MAJOR 4 +#define RAYGUI_VERSION_MINOR 5 +#define RAYGUI_VERSION_PATCH 0 +#define RAYGUI_VERSION "5.0-dev" + +#if !defined(RAYGUI_STANDALONE) + #include "raylib.h" +#endif + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RAYGUIAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RAYGUIAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif +#endif + +// Function specifiers definition +#ifndef RAYGUIAPI + #define RAYGUIAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Simple log system to avoid printf() calls if required +// NOTE: Avoiding those calls, also avoids const strings memory usage +#define RAYGUI_SUPPORT_LOG_INFO +#if defined(RAYGUI_SUPPORT_LOG_INFO) + #define RAYGUI_LOG(...) printf(__VA_ARGS__) +#else + #define RAYGUI_LOG(...) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Some types are required for RAYGUI_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + #ifndef __cplusplus + // Boolean type + #ifndef true + typedef enum { false, true } bool; + #endif + #endif + + // Vector2 type + typedef struct Vector2 { + float x; + float y; + } Vector2; + + // Vector3 type // -- ConvertHSVtoRGB(), ConvertRGBtoHSV() + typedef struct Vector3 { + float x; + float y; + float z; + } Vector3; + + // Color type, RGBA (32bit) + typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + } Color; + + // Rectangle type + typedef struct Rectangle { + float x; + float y; + float width; + float height; + } Rectangle; + + // TODO: Texture2D type is very coupled to raylib, required by Font type + // It should be redesigned to be provided by user + typedef struct Texture { + unsigned int id; // OpenGL texture id + int width; // Texture base width + int height; // Texture base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) + } Texture; + + // Texture2D, same as Texture + typedef Texture Texture2D; + + // Image, pixel data stored in CPU memory (RAM) + typedef struct Image { + void *data; // Image raw data + int width; // Image base width + int height; // Image base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) + } Image; + + // GlyphInfo, font characters glyphs info + typedef struct GlyphInfo { + int value; // Character value (Unicode) + int offsetX; // Character offset X when drawing + int offsetY; // Character offset Y when drawing + int advanceX; // Character advance position X + Image image; // Character image data + } GlyphInfo; + + // TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle() + // It should be redesigned to be provided by user + typedef struct Font { + int baseSize; // Base size (default chars height) + int glyphCount; // Number of glyph characters + int glyphPadding; // Padding around the glyph characters + Texture2D texture; // Texture atlas containing the glyphs + Rectangle *recs; // Rectangles in texture for the glyphs + GlyphInfo *glyphs; // Glyphs info data + } Font; +#endif + +// Style property +// NOTE: Used when exporting style as code for convenience +typedef struct GuiStyleProp { + unsigned short controlId; // Control identifier + unsigned short propertyId; // Property identifier + int propertyValue; // Property value +} GuiStyleProp; + +/* +// Controls text style -NOT USED- +// NOTE: Text style is defined by control +typedef struct GuiTextStyle { + unsigned int size; + int charSpacing; + int lineSpacing; + int alignmentH; + int alignmentV; + int padding; +} GuiTextStyle; +*/ + +// Gui control state +typedef enum { + STATE_NORMAL = 0, + STATE_FOCUSED, + STATE_PRESSED, + STATE_DISABLED +} GuiState; + +// Gui control text alignment +typedef enum { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_CENTER, + TEXT_ALIGN_RIGHT +} GuiTextAlignment; + +// Gui control text alignment vertical +// NOTE: Text vertical position inside the text bounds +typedef enum { + TEXT_ALIGN_TOP = 0, + TEXT_ALIGN_MIDDLE, + TEXT_ALIGN_BOTTOM +} GuiTextAlignmentVertical; + +// Gui control text wrap mode +// NOTE: Useful for multiline text +typedef enum { + TEXT_WRAP_NONE = 0, + TEXT_WRAP_CHAR, + TEXT_WRAP_WORD +} GuiTextWrapMode; + +// Gui controls +typedef enum { + // Default -> populates to all controls when set + DEFAULT = 0, + + // Basic controls + LABEL, // Used also for: LABELBUTTON + BUTTON, + TOGGLE, // Used also for: TOGGLEGROUP + SLIDER, // Used also for: SLIDERBAR, TOGGLESLIDER + PROGRESSBAR, + CHECKBOX, + COMBOBOX, + DROPDOWNBOX, + TEXTBOX, // Used also for: TEXTBOXMULTI + VALUEBOX, + CONTROL11, + LISTVIEW, + COLORPICKER, + SCROLLBAR, + STATUSBAR +} GuiControl; + +// Gui base properties for every control +// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties) +typedef enum { + BORDER_COLOR_NORMAL = 0, // Control border color in STATE_NORMAL + BASE_COLOR_NORMAL, // Control base color in STATE_NORMAL + TEXT_COLOR_NORMAL, // Control text color in STATE_NORMAL + BORDER_COLOR_FOCUSED, // Control border color in STATE_FOCUSED + BASE_COLOR_FOCUSED, // Control base color in STATE_FOCUSED + TEXT_COLOR_FOCUSED, // Control text color in STATE_FOCUSED + BORDER_COLOR_PRESSED, // Control border color in STATE_PRESSED + BASE_COLOR_PRESSED, // Control base color in STATE_PRESSED + TEXT_COLOR_PRESSED, // Control text color in STATE_PRESSED + BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED + BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED + TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED + BORDER_WIDTH = 12, // Control border size, 0 for no border + //TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls + //TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls + //TEXT_LINE_SPACING, // Control text spacing between lines -> GLOBAL for all controls + TEXT_PADDING = 13, // Control text padding, not considering border + TEXT_ALIGNMENT = 14, // Control text horizontal alignment inside control text bound (after border and padding) + //TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls +} GuiControlProperty; + +// TODO: Which text styling properties should be global or per-control? +// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while +// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and +// should be configured by user as needed while defining the UI layout + +// Gui extended properties depend on control +// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties) +//---------------------------------------------------------------------------------- +// DEFAULT extended properties +// NOTE: Those properties are common to all controls or global +// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT? +typedef enum { + TEXT_SIZE = 16, // Text size (glyphs max height) + TEXT_SPACING, // Text spacing between glyphs + LINE_COLOR, // Line control color + BACKGROUND_COLOR, // Background color + TEXT_LINE_SPACING, // Text spacing between lines + TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding) + TEXT_WRAP_MODE // Text wrap-mode inside text bounds + //TEXT_DECORATION // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline + //TEXT_DECORATION_THICK // Text decoration line thickness +} GuiDefaultProperty; + +// Other possible text properties: +// TEXT_WEIGHT // Normal, Italic, Bold -> Requires specific font change +// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING... + +// Label +//typedef enum { } GuiLabelProperty; + +// Button/Spinner +//typedef enum { } GuiButtonProperty; + +// Toggle/ToggleGroup +typedef enum { + GROUP_PADDING = 16, // ToggleGroup separation between toggles +} GuiToggleProperty; + +// Slider/SliderBar +typedef enum { + SLIDER_WIDTH = 16, // Slider size of internal bar + SLIDER_PADDING // Slider/SliderBar internal bar padding +} GuiSliderProperty; + +// ProgressBar +typedef enum { + PROGRESS_PADDING = 16, // ProgressBar internal padding +} GuiProgressBarProperty; + +// ScrollBar +typedef enum { + ARROWS_SIZE = 16, // ScrollBar arrows size + ARROWS_VISIBLE, // ScrollBar arrows visible + SCROLL_SLIDER_PADDING, // ScrollBar slider internal padding + SCROLL_SLIDER_SIZE, // ScrollBar slider size + SCROLL_PADDING, // ScrollBar scroll padding from arrows + SCROLL_SPEED, // ScrollBar scrolling speed +} GuiScrollBarProperty; + +// CheckBox +typedef enum { + CHECK_PADDING = 16 // CheckBox internal check padding +} GuiCheckBoxProperty; + +// ComboBox +typedef enum { + COMBO_BUTTON_WIDTH = 16, // ComboBox right button width + COMBO_BUTTON_SPACING // ComboBox button separation +} GuiComboBoxProperty; + +// DropdownBox +typedef enum { + ARROW_PADDING = 16, // DropdownBox arrow separation from border and items + DROPDOWN_ITEMS_SPACING, // DropdownBox items separation + DROPDOWN_ARROW_HIDDEN, // DropdownBox arrow hidden + DROPDOWN_ROLL_UP // DropdownBox roll up flag (default rolls down) +} GuiDropdownBoxProperty; + +// TextBox/TextBoxMulti/ValueBox/Spinner +typedef enum { + TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable +} GuiTextBoxProperty; + +// ValueBox/Spinner +typedef enum { + SPINNER_BUTTON_WIDTH = 16, // Spinner left/right buttons width + SPINNER_BUTTON_SPACING, // Spinner buttons separation +} GuiValueBoxProperty; + +// Control11 +//typedef enum { } GuiControl11Property; + +// ListView +typedef enum { + LIST_ITEMS_HEIGHT = 16, // ListView items height + LIST_ITEMS_SPACING, // ListView items separation + SCROLLBAR_WIDTH, // ListView scrollbar size (usually width) + SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE) + LIST_ITEMS_BORDER_NORMAL, // ListView items border enabled in normal state + LIST_ITEMS_BORDER_WIDTH // ListView items border width +} GuiListViewProperty; + +// ColorPicker +typedef enum { + COLOR_SELECTOR_SIZE = 16, + HUEBAR_WIDTH, // ColorPicker right hue bar width + HUEBAR_PADDING, // ColorPicker right hue bar separation from panel + HUEBAR_SELECTOR_HEIGHT, // ColorPicker right hue bar selector height + HUEBAR_SELECTOR_OVERFLOW // ColorPicker right hue bar selector overflow +} GuiColorPickerProperty; + +#define SCROLLBAR_LEFT_SIDE 0 +#define SCROLLBAR_RIGHT_SIDE 1 + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +#if defined(__cplusplus) +extern "C" { // Prevents name mangling of functions +#endif + +// Global gui state control functions +RAYGUIAPI void GuiEnable(void); // Enable gui controls (global state) +RAYGUIAPI void GuiDisable(void); // Disable gui controls (global state) +RAYGUIAPI void GuiLock(void); // Lock gui controls (global state) +RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state) +RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state) +RAYGUIAPI void GuiSetAlpha(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +RAYGUIAPI void GuiSetState(int state); // Set gui state (global state) +RAYGUIAPI int GuiGetState(void); // Get gui state (global state) + +// Font set/get functions +RAYGUIAPI void GuiSetFont(Font font); // Set gui custom font (global state) +RAYGUIAPI Font GuiGetFont(void); // Get gui custom font (global state) + +// Style set/get functions +RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property +RAYGUIAPI int GuiGetStyle(int control, int property); // Get one style property + +// Styles loading functions +RAYGUIAPI void GuiLoadStyle(const char *fileName); // Load style file over global style variable (.rgs) +RAYGUIAPI void GuiLoadStyleDefault(void); // Load style default over global style + +// Tooltips management functions +RAYGUIAPI void GuiEnableTooltip(void); // Enable gui tooltips (global state) +RAYGUIAPI void GuiDisableTooltip(void); // Disable gui tooltips (global state) +RAYGUIAPI void GuiSetTooltip(const char *tooltip); // Set tooltip string + +// Icons functionality +RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported) +#if !defined(RAYGUI_NO_ICONS) +RAYGUIAPI void GuiSetIconScale(int scale); // Set default icon drawing size +RAYGUIAPI unsigned int *GuiGetIcons(void); // Get raygui icons data pointer +RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data +RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position +#endif + +// Utility functions +RAYGUIAPI int GuiGetTextWidth(const char *text); // Get text width considering gui style and icon size (if required) + +// Controls +//---------------------------------------------------------------------------------------------------------- +// Container/separator controls, useful for controls organization +RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed +RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name +RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text +RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls +RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control + +// Basic controls set +RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control +RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked +RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, returns true when clicked +RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control +RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control +RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control +RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active +RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control + +RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control +RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control +RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers +RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values +RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text + +RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control +RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control +RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control +RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text +RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders +RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control + +// Advance controls set +RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control +RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message +RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret +RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) +RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color); // Color Panel control +RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control +RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control +RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Picker control that avoids conversion to RGB on each call (multiple color controls) +RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV() +//---------------------------------------------------------------------------------------------------------- + +#if !defined(RAYGUI_NO_ICONS) + +#if !defined(RAYGUI_CUSTOM_ICONS) +//---------------------------------------------------------------------------------- +// Icons enumeration +//---------------------------------------------------------------------------------- +typedef enum { + ICON_NONE = 0, + ICON_FOLDER_FILE_OPEN = 1, + ICON_FILE_SAVE_CLASSIC = 2, + ICON_FOLDER_OPEN = 3, + ICON_FOLDER_SAVE = 4, + ICON_FILE_OPEN = 5, + ICON_FILE_SAVE = 6, + ICON_FILE_EXPORT = 7, + ICON_FILE_ADD = 8, + ICON_FILE_DELETE = 9, + ICON_FILETYPE_TEXT = 10, + ICON_FILETYPE_AUDIO = 11, + ICON_FILETYPE_IMAGE = 12, + ICON_FILETYPE_PLAY = 13, + ICON_FILETYPE_VIDEO = 14, + ICON_FILETYPE_INFO = 15, + ICON_FILE_COPY = 16, + ICON_FILE_CUT = 17, + ICON_FILE_PASTE = 18, + ICON_CURSOR_HAND = 19, + ICON_CURSOR_POINTER = 20, + ICON_CURSOR_CLASSIC = 21, + ICON_PENCIL = 22, + ICON_PENCIL_BIG = 23, + ICON_BRUSH_CLASSIC = 24, + ICON_BRUSH_PAINTER = 25, + ICON_WATER_DROP = 26, + ICON_COLOR_PICKER = 27, + ICON_RUBBER = 28, + ICON_COLOR_BUCKET = 29, + ICON_TEXT_T = 30, + ICON_TEXT_A = 31, + ICON_SCALE = 32, + ICON_RESIZE = 33, + ICON_FILTER_POINT = 34, + ICON_FILTER_BILINEAR = 35, + ICON_CROP = 36, + ICON_CROP_ALPHA = 37, + ICON_SQUARE_TOGGLE = 38, + ICON_SYMMETRY = 39, + ICON_SYMMETRY_HORIZONTAL = 40, + ICON_SYMMETRY_VERTICAL = 41, + ICON_LENS = 42, + ICON_LENS_BIG = 43, + ICON_EYE_ON = 44, + ICON_EYE_OFF = 45, + ICON_FILTER_TOP = 46, + ICON_FILTER = 47, + ICON_TARGET_POINT = 48, + ICON_TARGET_SMALL = 49, + ICON_TARGET_BIG = 50, + ICON_TARGET_MOVE = 51, + ICON_CURSOR_MOVE = 52, + ICON_CURSOR_SCALE = 53, + ICON_CURSOR_SCALE_RIGHT = 54, + ICON_CURSOR_SCALE_LEFT = 55, + ICON_UNDO = 56, + ICON_REDO = 57, + ICON_REREDO = 58, + ICON_MUTATE = 59, + ICON_ROTATE = 60, + ICON_REPEAT = 61, + ICON_SHUFFLE = 62, + ICON_EMPTYBOX = 63, + ICON_TARGET = 64, + ICON_TARGET_SMALL_FILL = 65, + ICON_TARGET_BIG_FILL = 66, + ICON_TARGET_MOVE_FILL = 67, + ICON_CURSOR_MOVE_FILL = 68, + ICON_CURSOR_SCALE_FILL = 69, + ICON_CURSOR_SCALE_RIGHT_FILL = 70, + ICON_CURSOR_SCALE_LEFT_FILL = 71, + ICON_UNDO_FILL = 72, + ICON_REDO_FILL = 73, + ICON_REREDO_FILL = 74, + ICON_MUTATE_FILL = 75, + ICON_ROTATE_FILL = 76, + ICON_REPEAT_FILL = 77, + ICON_SHUFFLE_FILL = 78, + ICON_EMPTYBOX_SMALL = 79, + ICON_BOX = 80, + ICON_BOX_TOP = 81, + ICON_BOX_TOP_RIGHT = 82, + ICON_BOX_RIGHT = 83, + ICON_BOX_BOTTOM_RIGHT = 84, + ICON_BOX_BOTTOM = 85, + ICON_BOX_BOTTOM_LEFT = 86, + ICON_BOX_LEFT = 87, + ICON_BOX_TOP_LEFT = 88, + ICON_BOX_CENTER = 89, + ICON_BOX_CIRCLE_MASK = 90, + ICON_POT = 91, + ICON_ALPHA_MULTIPLY = 92, + ICON_ALPHA_CLEAR = 93, + ICON_DITHERING = 94, + ICON_MIPMAPS = 95, + ICON_BOX_GRID = 96, + ICON_GRID = 97, + ICON_BOX_CORNERS_SMALL = 98, + ICON_BOX_CORNERS_BIG = 99, + ICON_FOUR_BOXES = 100, + ICON_GRID_FILL = 101, + ICON_BOX_MULTISIZE = 102, + ICON_ZOOM_SMALL = 103, + ICON_ZOOM_MEDIUM = 104, + ICON_ZOOM_BIG = 105, + ICON_ZOOM_ALL = 106, + ICON_ZOOM_CENTER = 107, + ICON_BOX_DOTS_SMALL = 108, + ICON_BOX_DOTS_BIG = 109, + ICON_BOX_CONCENTRIC = 110, + ICON_BOX_GRID_BIG = 111, + ICON_OK_TICK = 112, + ICON_CROSS = 113, + ICON_ARROW_LEFT = 114, + ICON_ARROW_RIGHT = 115, + ICON_ARROW_DOWN = 116, + ICON_ARROW_UP = 117, + ICON_ARROW_LEFT_FILL = 118, + ICON_ARROW_RIGHT_FILL = 119, + ICON_ARROW_DOWN_FILL = 120, + ICON_ARROW_UP_FILL = 121, + ICON_AUDIO = 122, + ICON_FX = 123, + ICON_WAVE = 124, + ICON_WAVE_SINUS = 125, + ICON_WAVE_SQUARE = 126, + ICON_WAVE_TRIANGULAR = 127, + ICON_CROSS_SMALL = 128, + ICON_PLAYER_PREVIOUS = 129, + ICON_PLAYER_PLAY_BACK = 130, + ICON_PLAYER_PLAY = 131, + ICON_PLAYER_PAUSE = 132, + ICON_PLAYER_STOP = 133, + ICON_PLAYER_NEXT = 134, + ICON_PLAYER_RECORD = 135, + ICON_MAGNET = 136, + ICON_LOCK_CLOSE = 137, + ICON_LOCK_OPEN = 138, + ICON_CLOCK = 139, + ICON_TOOLS = 140, + ICON_GEAR = 141, + ICON_GEAR_BIG = 142, + ICON_BIN = 143, + ICON_HAND_POINTER = 144, + ICON_LASER = 145, + ICON_COIN = 146, + ICON_EXPLOSION = 147, + ICON_1UP = 148, + ICON_PLAYER = 149, + ICON_PLAYER_JUMP = 150, + ICON_KEY = 151, + ICON_DEMON = 152, + ICON_TEXT_POPUP = 153, + ICON_GEAR_EX = 154, + ICON_CRACK = 155, + ICON_CRACK_POINTS = 156, + ICON_STAR = 157, + ICON_DOOR = 158, + ICON_EXIT = 159, + ICON_MODE_2D = 160, + ICON_MODE_3D = 161, + ICON_CUBE = 162, + ICON_CUBE_FACE_TOP = 163, + ICON_CUBE_FACE_LEFT = 164, + ICON_CUBE_FACE_FRONT = 165, + ICON_CUBE_FACE_BOTTOM = 166, + ICON_CUBE_FACE_RIGHT = 167, + ICON_CUBE_FACE_BACK = 168, + ICON_CAMERA = 169, + ICON_SPECIAL = 170, + ICON_LINK_NET = 171, + ICON_LINK_BOXES = 172, + ICON_LINK_MULTI = 173, + ICON_LINK = 174, + ICON_LINK_BROKE = 175, + ICON_TEXT_NOTES = 176, + ICON_NOTEBOOK = 177, + ICON_SUITCASE = 178, + ICON_SUITCASE_ZIP = 179, + ICON_MAILBOX = 180, + ICON_MONITOR = 181, + ICON_PRINTER = 182, + ICON_PHOTO_CAMERA = 183, + ICON_PHOTO_CAMERA_FLASH = 184, + ICON_HOUSE = 185, + ICON_HEART = 186, + ICON_CORNER = 187, + ICON_VERTICAL_BARS = 188, + ICON_VERTICAL_BARS_FILL = 189, + ICON_LIFE_BARS = 190, + ICON_INFO = 191, + ICON_CROSSLINE = 192, + ICON_HELP = 193, + ICON_FILETYPE_ALPHA = 194, + ICON_FILETYPE_HOME = 195, + ICON_LAYERS_VISIBLE = 196, + ICON_LAYERS = 197, + ICON_WINDOW = 198, + ICON_HIDPI = 199, + ICON_FILETYPE_BINARY = 200, + ICON_HEX = 201, + ICON_SHIELD = 202, + ICON_FILE_NEW = 203, + ICON_FOLDER_ADD = 204, + ICON_ALARM = 205, + ICON_CPU = 206, + ICON_ROM = 207, + ICON_STEP_OVER = 208, + ICON_STEP_INTO = 209, + ICON_STEP_OUT = 210, + ICON_RESTART = 211, + ICON_BREAKPOINT_ON = 212, + ICON_BREAKPOINT_OFF = 213, + ICON_BURGER_MENU = 214, + ICON_CASE_SENSITIVE = 215, + ICON_REG_EXP = 216, + ICON_FOLDER = 217, + ICON_FILE = 218, + ICON_SAND_TIMER = 219, + ICON_WARNING = 220, + ICON_HELP_BOX = 221, + ICON_INFO_BOX = 222, + ICON_PRIORITY = 223, + ICON_LAYERS_ISO = 224, + ICON_LAYERS2 = 225, + ICON_MLAYERS = 226, + ICON_MAPS = 227, + ICON_HOT = 228, + ICON_LABEL = 229, + ICON_NAME_ID = 230, + ICON_SLICING = 231, + ICON_MANUAL_CONTROL = 232, + ICON_COLLISION = 233, + ICON_234 = 234, + ICON_235 = 235, + ICON_236 = 236, + ICON_237 = 237, + ICON_238 = 238, + ICON_239 = 239, + ICON_240 = 240, + ICON_241 = 241, + ICON_242 = 242, + ICON_243 = 243, + ICON_244 = 244, + ICON_245 = 245, + ICON_246 = 246, + ICON_247 = 247, + ICON_248 = 248, + ICON_249 = 249, + ICON_250 = 250, + ICON_251 = 251, + ICON_252 = 252, + ICON_253 = 253, + ICON_254 = 254, + ICON_255 = 255, +} GuiIconName; +#endif + +#endif + +#if defined(__cplusplus) +} // Prevents name mangling of functions +#endif + +#endif // RAYGUI_H + +/*********************************************************************************** +* +* RAYGUI IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RAYGUI_IMPLEMENTATION) + +#include // required for: isspace() [GuiTextBox()] +#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()] +#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy() +#include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()] +#include // Required for: roundf() [GuiColorPicker()] + +// Allow custom memory allocators +#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE) + #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE) + #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized" + #endif +#else + #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()] + + #define RAYGUI_MALLOC(sz) malloc(sz) + #define RAYGUI_CALLOC(n,sz) calloc(n,sz) + #define RAYGUI_FREE(p) free(p) +#endif + +#ifdef __cplusplus + #define RAYGUI_CLITERAL(name) name +#else + #define RAYGUI_CLITERAL(name) (name) +#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) + +// Embedded icons, no external file provided +#define RAYGUI_ICON_SIZE 16 // Size of icons in pixels (squared) +#define RAYGUI_ICON_MAX_ICONS 256 // Maximum number of icons +#define RAYGUI_ICON_MAX_NAME_LENGTH 32 // Maximum length of icon name id + +// Icons data is defined by bit array (every bit represents one pixel) +// Those arrays are stored as unsigned int data arrays, so, +// every array element defines 32 pixels (bits) of information +// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels) +// NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels) +#define RAYGUI_ICON_DATA_ELEMENTS (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32) + +//---------------------------------------------------------------------------------- +// Icons data for all gui possible icons (allocated on data segment by default) +// +// NOTE 1: Every icon is codified in binary form, using 1 bit per pixel, so, +// every 16x16 icon requires 8 integers (16*16/32) to be stored +// +// NOTE 2: A different icon set could be loaded over this array using GuiLoadIcons(), +// but loaded icons set must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS +// +// guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB +//---------------------------------------------------------------------------------- +static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_NONE + 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // ICON_FOLDER_FILE_OPEN + 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // ICON_FILE_SAVE_CLASSIC + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // ICON_FOLDER_OPEN + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // ICON_FOLDER_SAVE + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // ICON_FILE_OPEN + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // ICON_FILE_SAVE + 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // ICON_FILE_EXPORT + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // ICON_FILE_ADD + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // ICON_FILE_DELETE + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_FILETYPE_TEXT + 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // ICON_FILETYPE_AUDIO + 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // ICON_FILETYPE_IMAGE + 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // ICON_FILETYPE_PLAY + 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // ICON_FILETYPE_VIDEO + 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // ICON_FILETYPE_INFO + 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // ICON_FILE_COPY + 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // ICON_FILE_CUT + 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // ICON_FILE_PASTE + 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_CURSOR_HAND + 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000, // ICON_CURSOR_POINTER + 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // ICON_CURSOR_CLASSIC + 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000, // ICON_PENCIL + 0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // ICON_PENCIL_BIG + 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // ICON_BRUSH_CLASSIC + 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // ICON_BRUSH_PAINTER + 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // ICON_WATER_DROP + 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // ICON_COLOR_PICKER + 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // ICON_RUBBER + 0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // ICON_COLOR_BUCKET + 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // ICON_TEXT_T + 0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // ICON_TEXT_A + 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // ICON_SCALE + 0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // ICON_RESIZE + 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_POINT + 0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_BILINEAR + 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // ICON_CROP + 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // ICON_CROP_ALPHA + 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002, // ICON_SQUARE_TOGGLE + 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // ICON_SYMMETRY + 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // ICON_SYMMETRY_HORIZONTAL + 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // ICON_SYMMETRY_VERTICAL + 0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // ICON_LENS + 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // ICON_LENS_BIG + 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // ICON_EYE_ON + 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // ICON_EYE_OFF + 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100, // ICON_FILTER_TOP + 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // ICON_FILTER + 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_POINT + 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL + 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG + 0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // ICON_TARGET_MOVE + 0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // ICON_CURSOR_MOVE + 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // ICON_CURSOR_SCALE + 0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // ICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // ICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO + 0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // ICON_REREDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // ICON_MUTATE + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // ICON_ROTATE + 0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // ICON_REPEAT + 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // ICON_SHUFFLE + 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // ICON_EMPTYBOX + 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // ICON_TARGET + 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL_FILL + 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // ICON_TARGET_MOVE_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // ICON_CURSOR_MOVE_FILL + 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // ICON_CURSOR_SCALE_FILL + 0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // ICON_CURSOR_SCALE_RIGHT_FILL + 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // ICON_CURSOR_SCALE_LEFT_FILL + 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO_FILL + 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // ICON_REREDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // ICON_MUTATE_FILL + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // ICON_ROTATE_FILL + 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // ICON_REPEAT_FILL + 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // ICON_SHUFFLE_FILL + 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // ICON_EMPTYBOX_SMALL + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX + 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP + 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // ICON_BOX_BOTTOM_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // ICON_BOX_BOTTOM + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // ICON_BOX_BOTTOM_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_LEFT + 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_CENTER + 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // ICON_BOX_CIRCLE_MASK + 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // ICON_POT + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // ICON_ALPHA_MULTIPLY + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // ICON_ALPHA_CLEAR + 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // ICON_DITHERING + 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // ICON_MIPMAPS + 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // ICON_BOX_GRID + 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // ICON_GRID + 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // ICON_BOX_CORNERS_SMALL + 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // ICON_BOX_CORNERS_BIG + 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // ICON_FOUR_BOXES + 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // ICON_GRID_FILL + 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // ICON_BOX_MULTISIZE + 0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_SMALL + 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_MEDIUM + 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // ICON_ZOOM_BIG + 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // ICON_ZOOM_ALL + 0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // ICON_ZOOM_CENTER + 0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // ICON_BOX_DOTS_SMALL + 0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000, // ICON_BOX_DOTS_BIG + 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // ICON_BOX_CONCENTRIC + 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // ICON_BOX_GRID_BIG + 0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000, // ICON_OK_TICK + 0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000, // ICON_CROSS + 0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000, // ICON_ARROW_LEFT + 0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000, // ICON_ARROW_RIGHT + 0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000, // ICON_ARROW_DOWN + 0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP + 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // ICON_ARROW_LEFT_FILL + 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // ICON_ARROW_RIGHT_FILL + 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // ICON_ARROW_DOWN_FILL + 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP_FILL + 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // ICON_AUDIO + 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // ICON_FX + 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // ICON_WAVE + 0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000, // ICON_WAVE_SINUS + 0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // ICON_WAVE_SQUARE + 0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000, // ICON_WAVE_TRIANGULAR + 0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000, // ICON_CROSS_SMALL + 0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // ICON_PLAYER_PREVIOUS + 0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000, // ICON_PLAYER_PLAY_BACK + 0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000, // ICON_PLAYER_PLAY + 0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // ICON_PLAYER_PAUSE + 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // ICON_PLAYER_STOP + 0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // ICON_PLAYER_NEXT + 0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // ICON_PLAYER_RECORD + 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // ICON_MAGNET + 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_CLOSE + 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_OPEN + 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // ICON_CLOCK + 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // ICON_TOOLS + 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // ICON_GEAR + 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // ICON_GEAR_BIG + 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // ICON_BIN + 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // ICON_HAND_POINTER + 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // ICON_LASER + 0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // ICON_COIN + 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // ICON_EXPLOSION + 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // ICON_1UP + 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // ICON_PLAYER + 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // ICON_PLAYER_JUMP + 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // ICON_KEY + 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // ICON_DEMON + 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // ICON_TEXT_POPUP + 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // ICON_GEAR_EX + 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK + 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK_POINTS + 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // ICON_STAR + 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // ICON_DOOR + 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // ICON_EXIT + 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // ICON_MODE_2D + 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE + 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP + 0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT + 0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK + 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA + 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL + 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET + 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // ICON_LINK_BOXES + 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // ICON_LINK_MULTI + 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // ICON_LINK + 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // ICON_LINK_BROKE + 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_TEXT_NOTES + 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // ICON_NOTEBOOK + 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // ICON_SUITCASE + 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // ICON_SUITCASE_ZIP + 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // ICON_MAILBOX + 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // ICON_MONITOR + 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // ICON_PRINTER + 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA + 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA_FLASH + 0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // ICON_HOUSE + 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // ICON_HEART + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // ICON_CORNER + 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // ICON_VERTICAL_BARS + 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // ICON_VERTICAL_BARS_FILL + 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // ICON_LIFE_BARS + 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // ICON_INFO + 0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002, // ICON_CROSSLINE + 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // ICON_HELP + 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // ICON_FILETYPE_ALPHA + 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // ICON_FILETYPE_HOME + 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS_VISIBLE + 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS + 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_WINDOW + 0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // ICON_HIDPI + 0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc, // ICON_FILETYPE_BINARY + 0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000, // ICON_HEX + 0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180, // ICON_SHIELD + 0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8, // ICON_FILE_NEW + 0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000, // ICON_FOLDER_ADD + 0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420, // ICON_ALARM + 0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0, // ICON_CPU + 0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0, // ICON_ROM + 0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000, // ICON_STEP_OVER + 0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_INTO + 0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_OUT + 0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000, // ICON_RESTART + 0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000, // ICON_BREAKPOINT_ON + 0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000, // ICON_BREAKPOINT_OFF + 0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000, // ICON_BURGER_MENU + 0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000, // ICON_CASE_SENSITIVE + 0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000, // ICON_REG_EXP + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE + 0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0, // ICON_SAND_TIMER + 0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc, // ICON_WARNING + 0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe, // ICON_HELP_BOX + 0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe, // ICON_INFO_BOX + 0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0, // ICON_PRIORITY + 0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000, // ICON_LAYERS_ISO + 0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0, // ICON_LAYERS2 + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0, // ICON_MLAYERS + 0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0, // ICON_MAPS + 0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70, // ICON_HOT + 0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060, // ICON_LABEL + 0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000, // ICON_NAME_ID + 0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000, // ICON_SLICING + 0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0, // ICON_MANUAL_CONTROL + 0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe, // ICON_COLLISION + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_234 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_235 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_236 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_237 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_238 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_239 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_240 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_241 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_242 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_243 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_244 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_245 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_246 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_247 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_248 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_249 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_253 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_254 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255 +}; + +// NOTE: A pointer to current icons array should be defined +static unsigned int *guiIconsPtr = guiIcons; + +#endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS + +#ifndef RAYGUI_ICON_SIZE + #define RAYGUI_ICON_SIZE 0 +#endif + +// WARNING: Those values define the total size of the style data array, +// if changed, previous saved styles could become incompatible +#define RAYGUI_MAX_CONTROLS 16 // Maximum number of controls +#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of base properties +#define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Gui control property style color element +typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_NORMAL, forces defined state + +static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib) +static bool guiLocked = false; // Gui lock state (no inputs processed) +static float guiAlpha = 1.0f; // Gui controls transparency + +static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) + +static bool guiTooltip = false; // Tooltip enabled/disabled +static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user) + +static bool guiControlExclusiveMode = false; // Gui control exclusive mode (no inputs processed except current control) +static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier + +static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() +//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking +static int autoCursorCounter = 0; // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay) + +//---------------------------------------------------------------------------------- +// Style data array for all gui style properties (allocated on data segment by default) +// +// NOTE 1: First set of BASE properties are generic to all controls but could be individually +// overwritten per control, first set of EXTENDED properties are generic to all controls and +// can not be overwritten individually but custom EXTENDED properties can be used by control +// +// NOTE 2: A new style set could be loaded over this array using GuiLoadStyle(), +// but default gui style could always be recovered with GuiLoadStyleDefault() +// +// guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB +//---------------------------------------------------------------------------------- +static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 }; + +static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization + +//---------------------------------------------------------------------------------- +// Standalone Mode Functions Declaration +// +// NOTE: raygui depend on some raylib input and drawing functions +// To use raygui as standalone library, below functions must be defined by the user +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + +#define KEY_RIGHT 262 +#define KEY_LEFT 263 +#define KEY_DOWN 264 +#define KEY_UP 265 +#define KEY_BACKSPACE 259 +#define KEY_ENTER 257 + +#define MOUSE_LEFT_BUTTON 0 + +// Input required functions +//------------------------------------------------------------------------------- +static Vector2 GetMousePosition(void); +static float GetMouseWheelMove(void); +static bool IsMouseButtonDown(int button); +static bool IsMouseButtonPressed(int button); +static bool IsMouseButtonReleased(int button); + +static bool IsKeyDown(int key); +static bool IsKeyPressed(int key); +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +//------------------------------------------------------------------------------- + +// Drawing required functions +//------------------------------------------------------------------------------- +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +//------------------------------------------------------------------------------- + +// Text required functions +//------------------------------------------------------------------------------- +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font + +static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) + +static char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +static void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data + +static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs + +static int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +static void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list + +static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +//------------------------------------------------------------------------------- + +// raylib functions already implemented in raygui +//------------------------------------------------------------------------------- +static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value +static int ColorToInt(Color color); // Returns hexadecimal value for a Color +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle +static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' +static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +static int TextToInteger(const char *text); // Get integer value from text +static float TextToFloat(const char *text); // Get float value from text + +static int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded text +static const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode codepoint into UTF-8 text (char array size returned as parameter) + +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient +//------------------------------------------------------------------------------- + +#endif // RAYGUI_STANDALONE + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only) + +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 void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font +static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style + +static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings +static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB +static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV + +static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll bar control, used by GuiScrollPanel() +static void GuiTooltip(Rectangle controlRec); // Draw tooltip using control rec position + +static Color GuiFade(Color color, float alpha); // Fade color by an alpha factor + +//---------------------------------------------------------------------------------- +// Gui Setup Functions Definition +//---------------------------------------------------------------------------------- +// Enable gui global state +// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups +void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; } + +// Disable gui global state +// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups +void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; } + +// Lock gui global state +void GuiLock(void) { guiLocked = true; } + +// Unlock gui global state +void GuiUnlock(void) { guiLocked = false; } + +// Check if gui is locked (global state) +bool GuiIsLocked(void) { return guiLocked; } + +// Set gui controls alpha global state +void GuiSetAlpha(float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + guiAlpha = alpha; +} + +// Set gui state (global state) +void GuiSetState(int state) { guiState = (GuiState)state; } + +// Get gui state (global state) +int GuiGetState(void) { return guiState; } + +// Set custom gui font +// NOTE: Font loading/unloading is external to raygui +void GuiSetFont(Font font) +{ + if (font.texture.id > 0) + { + // NOTE: If we try to setup a font but default style has not been + // lazily loaded before, it will be overwritten, so we need to force + // default style loading first + if (!guiStyleLoaded) GuiLoadStyleDefault(); + + guiFont = font; + } +} + +// Get custom gui font +Font GuiGetFont(void) +{ + return guiFont; +} + +// Set control style property value +void GuiSetStyle(int control, int property, int value) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; + + // Default properties are propagated to all controls + if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE)) + { + for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value; + } +} + +// Get control style property value +int GuiGetStyle(int control, int property) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property]; +} + +//---------------------------------------------------------------------------------- +// Gui Controls Functions Definition +//---------------------------------------------------------------------------------- + +// Window Box control +int GuiWindowBox(Rectangle bounds, const char *title) +{ + // Window title bar height (including borders) + // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox() + #if !defined(RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT) + #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24 + #endif + + #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT) + #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT 18 + #endif + + int result = 0; + //GuiState state = guiState; + + int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; + + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight }; + if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f; + + const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f; + Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 }; + Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding, + statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT }; + + // Update control + //-------------------------------------------------------------------- + // NOTE: Logic is directly managed by button + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiStatusBar(statusBar, title); // Draw window header as status bar + GuiPanel(windowPanel, NULL); // Draw window base + + // Draw window close button + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); +#if defined(RAYGUI_NO_ICONS) + result = GuiButton(closeButtonRec, "x"); +#else + result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + //-------------------------------------------------------------------- + + return result; // Window close button clicked: result = 1 +} + +// Group Box control with text name +int GuiGroupBox(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_GROUPBOX_LINE_THICK) + #define RAYGUI_GROUPBOX_LINE_THICK 1 + #endif + + int result = 0; + GuiState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR))); + + GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text); + //-------------------------------------------------------------------- + + return result; +} + +// Line control +int GuiLine(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_LINE_MARGIN_TEXT) + #define RAYGUI_LINE_MARGIN_TEXT 12 + #endif + #if !defined(RAYGUI_LINE_TEXT_PADDING) + #define RAYGUI_LINE_TEXT_PADDING 4 + #endif + + int result = 0; + GuiState state = guiState; + + Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)); + + // Draw control + //-------------------------------------------------------------------- + if (text == NULL) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, bounds.width, 1 }, 0, BLANK, color); + else + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = bounds.height; + textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT; + textBounds.y = bounds.y; + + // Draw line with embedded text label: "--- text --------------" + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color); + GuiDrawText(text, textBounds, TEXT_ALIGN_LEFT, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color); + } + //-------------------------------------------------------------------- + + return result; +} + +// Panel control +int GuiPanel(Rectangle bounds, const char *text) +{ + #if !defined(RAYGUI_PANEL_BORDER_WIDTH) + #define RAYGUI_PANEL_BORDER_WIDTH 1 + #endif + + int result = 0; + GuiState state = guiState; + + // Text will be drawn as a header bar (if provided) + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; + if ((text != NULL) && (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f)) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f; + + if (text != NULL) + { + // Move panel bounds after the header bar + bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + } + + // Draw control + //-------------------------------------------------------------------- + if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar + + GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)), + GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR))); + //-------------------------------------------------------------------- + + return result; +} + +// Tab Bar control +// NOTE: Using GuiToggle() for the TABS +int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) +{ + #define RAYGUI_TABBAR_ITEM_WIDTH 160 + + int result = -1; + //GuiState state = guiState; + + Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height }; + + if (*active < 0) *active = 0; + else if (*active > count - 1) *active = count - 1; + + int offsetX = 0; // Required in case tabs go out of screen + offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth(); + if (offsetX < 0) offsetX = 0; + + bool toggle = false; // Required for individual toggles + + // Draw control + //-------------------------------------------------------------------- + for (int i = 0; i < count; i++) + { + tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX; + + if (tabBounds.x < GetScreenWidth()) + { + // Draw tabs as toggle controls + int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT); + int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(TOGGLE, TEXT_PADDING, 8); + + if (i == (*active)) + { + toggle = true; + GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle); + } + else + { + toggle = false; + GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle); + if (toggle) *active = i; + } + + // Close tab with middle mouse button pressed + if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i; + + GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); + + // Draw tab close button + // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds)) + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); +#if defined(RAYGUI_NO_ICONS) + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, "x")) result = i; +#else + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) result = i; +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + } + } + + // Draw tab-bar bottom line + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL))); + //-------------------------------------------------------------------- + + return result; // Return as result the current TAB closing requested +} + +// Scroll Panel control +int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view) +{ + #define RAYGUI_MIN_SCROLLBAR_WIDTH 40 + #define RAYGUI_MIN_SCROLLBAR_HEIGHT 40 + #define RAYGUI_MIN_MOUSE_WHEEL_SPEED 20 + + int result = 0; + GuiState state = guiState; + + Rectangle temp = { 0 }; + if (view == NULL) view = &temp; + + Vector2 scrollPos = { 0.0f, 0.0f }; + if (scroll != NULL) scrollPos = *scroll; + + // Text will be drawn as a header bar (if provided) + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT }; + if (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f; + + if (text != NULL) + { + // Move panel bounds after the header bar + bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1; + } + + bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + + // Recheck to account for the other scrollbar being visible + if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + + int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; + Rectangle horizontalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)horizontalScrollBarWidth + }; + Rectangle verticalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), + (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)verticalScrollBarWidth, + (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Make sure scroll bars have a minimum width/height + if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; + if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; + + // Calculate view area (area without the scrollbars) + *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? + RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } : + RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth }; + + // Clip view area to the actual content size + if (view->width > content.width) view->width = content.width; + if (view->height > content.height) view->height = content.height; + + float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH); + float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f; + float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else state = STATE_FOCUSED; + +#if defined(SUPPORT_SCROLLBAR_KEY_INPUT) + if (hasHorizontalScrollBar) + { + if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } + + if (hasVerticalScrollBar) + { + if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } +#endif + float wheelMove = GetMouseWheelMove(); + + // Set scrolling speed with mouse wheel based on ratio between bounds and content + Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height }; + if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED; + + // Horizontal and vertical scrolling with mouse wheel + if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x; + else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll + } + } + + // Normalize scroll values + if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin; + if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax; + if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin; + if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar + + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Save size of the scrollbar slider + const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + + // Draw horizontal scrollbar if visible + if (hasHorizontalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content width and the widget width + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/(int)content.width)*((int)bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth))); + scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, (int)horizontalMin, (int)horizontalMax); + } + else scrollPos.x = 0.0f; + + // Draw vertical scrollbar if visible + if (hasVerticalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content height and the widget height + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/(int)content.height)*((int)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth))); + scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, (int)verticalMin, (int)verticalMax); + } + else scrollPos.y = 0.0f; + + // Draw detail corner rectangle if both scroll bars are visible + if (hasHorizontalScrollBar && hasVerticalScrollBar) + { + Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 }; + GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3)))); + } + + // Draw scrollbar lines depending on current state + GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK); + + // Set scrollbar slider size back to the way it was before + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); + //-------------------------------------------------------------------- + + if (scroll != NULL) *scroll = scrollPos; + + return result; +} + +// Label control +int GuiLabel(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + //... + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Button control, returns true when clicked +int GuiButton(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3)))); + + if (state == STATE_FOCUSED) GuiTooltip(bounds); + //------------------------------------------------------------------ + + return result; // Button pressed: result = 1 +} + +// Label button control +int GuiLabelButton(Rectangle bounds, const char *text) +{ + GuiState state = guiState; + bool pressed = false; + + // NOTE: We force bounds.width to be all text + float textWidth = (float)GuiGetTextWidth(text); + 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 + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return pressed; +} + +// Toggle Button control +int GuiToggle(Rectangle bounds, const char *text, bool *active) +{ + int result = 0; + GuiState state = guiState; + + bool temp = false; + if (active == NULL) active = &temp; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + // Check toggle button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + state = STATE_NORMAL; + *active = !(*active); + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_NORMAL) + { + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3))))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3))))); + } + else + { + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3))); + } + + if (state == STATE_FOCUSED) GuiTooltip(bounds); + //-------------------------------------------------------------------- + + return result; +} + +// Toggle Group control +int GuiToggleGroup(Rectangle bounds, const char *text, int *active) +{ + #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS) + #define RAYGUI_TOGGLEGROUP_MAX_ITEMS 32 + #endif + + int result = 0; + float initBoundsX = bounds.x; + + int temp = 0; + if (active == NULL) active = &temp; + + bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, rows); + + int prevRow = rows[0]; + + for (int i = 0; i < itemCount; i++) + { + if (prevRow != rows[i]) + { + bounds.x = initBoundsX; + bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING)); + prevRow = rows[i]; + } + + if (i == (*active)) + { + toggle = true; + GuiToggle(bounds, items[i], &toggle); + } + else + { + toggle = false; + GuiToggle(bounds, items[i], &toggle); + if (toggle) *active = i; + } + + bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING)); + } + + return result; +} + +// Toggle Slider control extended +int GuiToggleSlider(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + //bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int itemCount = 0; + const char **items = NULL; + + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle slider = { + 0, // Calculated later depending on the active toggle + bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount, + bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + (*active)++; + result = 1; + } + else state = STATE_FOCUSED; + } + + if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED; + } + + if (*active >= itemCount) *active = 0; + slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))), + GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL))); + + // Draw internal slider + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + + // Draw text in slider + if (text != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = slider.x + slider.width/2 - textBounds.width/2; + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return result; +} + +// Check Box control, returns 1 when state changed +int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) +{ + int result = 0; + GuiState state = guiState; + + bool temp = false; + if (checked == NULL) checked = &temp; + + Rectangle textBounds = { 0 }; + + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + Rectangle totalBounds = { + (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x, + bounds.y, + bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING), + bounds.height, + }; + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, totalBounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else state = STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + *checked = !(*checked); + result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK); + + if (*checked) + { + Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), + bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)), + bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) }; + GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3))); + } + + GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Combo Box control +int GuiComboBox(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING)); + + Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING), + (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height }; + + // Get substrings items from text (items pointers, lengths and count) + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + if (*active < 0) *active = 0; + else if (*active > (itemCount - 1)) *active = itemCount - 1; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + *active += 1; + if (*active >= itemCount) *active = 0; // Cyclic combobox + } + + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw combo box main + GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3)))); + GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3)))); + + // Draw selector using a custom button + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount)); + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + //-------------------------------------------------------------------- + + return result; +} + +// Dropdown Box control +// NOTE: Returns mouse click +int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + int itemSelected = *active; + int itemFocused = -1; + + int direction = 0; // Dropdown box open direction: down (default) + if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up + + // Get substrings items from text (items pointers, lengths and count) + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle boundsOpen = bounds; + boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING); + + Rectangle itemBounds = bounds; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) + { + state = STATE_PRESSED; + + // Check if mouse has been pressed or released outside limits + if (!CheckCollisionPointRec(mousePoint, boundsOpen)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; + } + + // Check if already selected item has been pressed again + if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + + // Check focused and selected item + for (int i = 0; i < itemCount; i++) + { + // Update item rectangle y position for next item + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = i; + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + itemSelected = i; + result = 1; // Item selected + } + break; + } + } + + itemBounds = bounds; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + result = 1; + state = STATE_PRESSED; + } + else state = STATE_FOCUSED; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (editMode) GuiPanel(boundsOpen, NULL); + + GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3))); + GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3))); + + if (editMode) + { + // Draw visible items + for (int i = 0; i < itemCount; i++) + { + // Update item rectangle y position for next item + if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); + + if (i == itemSelected) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED))); + } + else if (i == itemFocused) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED))); + } + else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL))); + } + } + + if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN)) + { + // Draw arrows (using icon if available) +#if defined(RAYGUI_NO_ICONS) + GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); +#else + GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL +#endif + } + //-------------------------------------------------------------------- + + *active = itemSelected; + + // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item... + return result; // Mouse click: result = 1 +} + +// Text Box control +// NOTE: Returns true on ENTER pressed (useful for data validation) +int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +{ + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 30 // Frames to wait for autocursor movement + #endif + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 2 // Frames delay for autocursor movement + #endif + + int result = 0; + GuiState state = guiState; + + bool multiline = false; // TODO: Consider multiline text input + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); + + Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); + int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length + int thisCursorIndex = textBoxCursorIndex; + if (thisCursorIndex > textLength) thisCursorIndex = textLength; + int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex); + int textIndexOffset = 0; // Text index offset to start drawing in the box + + // 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 + }; + + 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); + + // Mouse cursor rectangle + // NOTE: Initialized outside of screen + Rectangle mouseCursor = cursor; + mouseCursor.x = -1; + mouseCursor.width = 1; + + // Blink-cursor frame counter + //if (!autoCursorMode) blinkCursorFrameCounter++; + //else blinkCursorFrameCounter = 0; + + // Update control + //-------------------------------------------------------------------- + // WARNING: Text editing is only supported under certain conditions: + if ((state != STATE_DISABLED) && // Control not disabled + !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode + !guiLocked && // Gui not locked + !guiControlExclusiveMode && // No gui slider on dragging + (wrapMode == TEXT_WRAP_NONE)) // No wrap mode + { + Vector2 mousePosition = GetMousePosition(); + + if (editMode) + { + // GLOBAL: Auto-cursor movement logic + // NOTE: Keystrokes are handled repeatedly when button is held down for some time + if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++; + else autoCursorCounter = 0; + + bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0); + + state = STATE_PRESSED; + + if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; + + // If text does not fit in the textbox and current cursor position is out of bounds, + // we add an index offset to text for drawing only what requires depending on cursor + while (textWidth >= textBounds.width) + { + int nextCodepointSize = 0; + GetCodepointNext(text + textIndexOffset, &nextCodepointSize); + + textIndexOffset += nextCodepointSize; + + textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex); + } + + int codepoint = GetCharPressed(); // Get Unicode codepoint + if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; + + // Encode codepoint as UTF-8 + int codepointSize = 0; + const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); + + // Handle Paste action + if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + { + const char *pasteText = GetClipboardText(); + if (pasteText != NULL) + { + int pasteLength = 0; + int pasteCodepoint; + int pasteCodepointSize; + // count how many codepoints to copy, stopping at the first unwanted control character + while (true) + { + pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize); + if (textLength + pasteLength + pasteCodepointSize >= textSize) break; + if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break; + pasteLength += pasteCodepointSize; + } + if (pasteLength > 0) + { + // Move forward data from cursor position + for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength]; + + // Paste data in at cursor + for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i]; + + textBoxCursorIndex += pasteLength; + textLength += pasteLength; + text[textLength] = '\0'; + } + } + } + // Add codepoint to text, at current cursor position + // NOTE: Make sure we do not overflow buffer size + else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) + { + // Move forward data from cursor position + for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize]; + + // Add new codepoint in current cursor position + for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i]; + + textBoxCursorIndex += codepointSize; + textLength += codepointSize; + + // Make sure text last character is EOL + text[textLength] = '\0'; + } + + // 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 related codepoints from text, after current cursor position + if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int nextCodepointSize; + int nextCodepoint; + // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + bool puctuation = ispunct(nextCodepoint & 0xff); + while (offset < textLength) + { + if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) + break; + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + // Check whitespace to delete (ASCII only) + while (offset < textLength) + { + if (!isspace(nextCodepoint & 0xff)) + break; + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Move text after cursor forward (including final null terminator) + for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i]; + + textLength -= accCodepointSize; + } + // Delete single codepoint from text, after current cursor position + else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) + { + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i]; + + textLength -= nextCodepointSize; + } + + // Delete related codepoints from text, before current cursor position + if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int prevCodepointSize; + int prevCodepoint; + + // Check whitespace to delete (ASCII only) + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if (!isspace(prevCodepoint & 0xff)) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + bool puctuation = ispunct(prevCodepoint & 0xff); + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i]; + + textLength -= accCodepointSize; + textBoxCursorIndex -= accCodepointSize; + } + // Delete single codepoint from text, before current cursor position + else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) + { + int prevCodepointSize = 0; + + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + // Move text after cursor forward (including final null terminator) + for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i]; + + textLength -= prevCodepointSize; + textBoxCursorIndex -= prevCodepointSize; + } + + // Move cursor position with keys + if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int prevCodepointSize; + int prevCodepoint; + + // Check whitespace to skip (ASCII only) + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if (!isspace(prevCodepoint & 0xff)) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + + // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + bool puctuation = ispunct(prevCodepoint & 0xff); + while (offset > 0) + { + prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize); + if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break; + + offset -= prevCodepointSize; + accCodepointSize += prevCodepointSize; + } + + textBoxCursorIndex = offset; + } + else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger))) + { + int prevCodepointSize = 0; + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + textBoxCursorIndex -= prevCodepointSize; + } + else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))) + { + int offset = textBoxCursorIndex; + int accCodepointSize = 0; + int nextCodepointSize; + int nextCodepoint; + + // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace) + // Not using isalnum() since it only works on ASCII characters + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + bool puctuation = ispunct(nextCodepoint & 0xff); + while (offset < textLength) + { + if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break; + + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + // Check whitespace to skip (ASCII only) + while (offset < textLength) + { + if (!isspace(nextCodepoint & 0xff)) break; + + offset += nextCodepointSize; + accCodepointSize += nextCodepointSize; + nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize); + } + + textBoxCursorIndex = offset; + } + else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger))) + { + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + textBoxCursorIndex += nextCodepointSize; + } + + // Move cursor position with mouse + if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text + { + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; + int codepointIndex = 0; + float glyphWidth = 0.0f; + float widthToMouseX = 0; + int mouseCursorIndex = 0; + + for (int i = textIndexOffset; i < textLength; i += codepointSize) + { + codepoint = GetCodepointNext(&text[i], &codepointSize); + codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2))) + { + mouseCursor.x = textBounds.x + widthToMouseX; + mouseCursorIndex = i; + break; + } + + widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + + // Check if mouse cursor is at the last position + int textEndWidth = GuiGetTextWidth(text + textIndexOffset); + if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) + { + mouseCursor.x = textBounds.x + textEndWidth; + mouseCursorIndex = textLength; + } + + // Place cursor at required index on mouse click + if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + cursor.x = mouseCursor.x; + textBoxCursorIndex = mouseCursorIndex; + } + } + else mouseCursor.x = -1; + + // Recalculate cursor position.y depending on textBoxCursorIndex + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); + //if (multiline) cursor.y = GetTextLines() + + // Finish text editing on ENTER or mouse click outside bounds + if ((!multiline && IsKeyPressed(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + { + textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index + autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes + result = 1; + } + } + else + { + if (CheckCollisionPointRec(mousePosition, bounds)) + { + state = STATE_FOCUSED; + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + textBoxCursorIndex = textLength; // GLOBAL: Place cursor index to the end of current text + autoCursorCounter = 0; // GLOBAL: Reset counter for repeated keystrokes + result = 1; + } + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_PRESSED) + { + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); + } + else if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); + } + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); + + // Draw text considering index offset if required + // NOTE: Text index offset depends on cursor position + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); + + // Draw cursor + if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) + { + //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + + // Draw mouse position cursor (if required) + if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + } + else if (state == STATE_FOCUSED) GuiTooltip(bounds); + //-------------------------------------------------------------------- + + return result; // Mouse button pressed: result = 1 +} + +/* +// Text Box control with multiple lines and word-wrap +// NOTE: This text-box is readonly, no editing supported by default +bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) +{ + bool pressed = false; + + GuiSetStyle(TEXTBOX, TEXT_READONLY, 1); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // WARNING: If wrap mode enabled, text editing is not supported + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); + + // TODO: Implement methods to calculate cursor position properly + pressed = GuiTextBox(bounds, text, textSize, editMode); + + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 0); + + return pressed; +} +*/ + +// Spinner control, returns selected value +int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +{ + int result = 1; + GuiState state = guiState; + + int tempValue = *value; + + Rectangle valueBoxBounds = { + bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING), + bounds.y, + bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height }; + Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height }; + Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y, + (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height }; + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + // Check spinner state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + +#if defined(RAYGUI_NO_ICONS) + if (GuiButton(leftButtonBound, "<")) tempValue--; + if (GuiButton(rightButtonBound, ">")) tempValue++; +#else + if (GuiButton(leftButtonBound, GuiIconText(ICON_ARROW_LEFT_FILL, NULL))) tempValue--; + if (GuiButton(rightButtonBound, GuiIconText(ICON_ARROW_RIGHT_FILL, NULL))) tempValue++; +#endif + + if (!editMode) + { + if (tempValue < minValue) tempValue = minValue; + if (tempValue > maxValue) tempValue = maxValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode); + + // Draw value selector custom buttons + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH)); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + + // Draw text label if provided + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + *value = tempValue; + return result; +} + +// Value Box control, updates input text with numbers +// NOTE: Requires static variables: frameCounter +int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +{ + #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) + #define RAYGUI_VALUEBOX_MAX_CHARS 32 + #endif + + int result = 0; + GuiState state = guiState; + + char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; + snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value); + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + bool valueHasChanged = false; + + if (editMode) + { + state = STATE_PRESSED; + + int keyCount = (int)strlen(textValue); + + // Add or remove minus symbol + if (IsKeyPressed(KEY_MINUS)) + { + if (textValue[0] == '-') + { + for (int i = 0 ; i < keyCount; i++) + { + textValue[i] = textValue[i + 1]; + } + keyCount--; + valueHasChanged = true; + } + else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS -1) + { + if (keyCount == 0) + { + textValue[0] = '0'; + textValue[1] = '\0'; + keyCount++; + } + + for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i]; + + textValue[0] = '-'; + keyCount++; + valueHasChanged = true; + } + } + + // Only allow keys in range [48..57] + if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (GuiGetTextWidth(textValue) < bounds.width) + { + int key = GetCharPressed(); + if ((key >= 48) && (key <= 57)) + { + textValue[keyCount] = (char)key; + keyCount++; + valueHasChanged = true; + } + } + } + + // Delete text + if (keyCount > 0) + { + if (IsKeyPressed(KEY_BACKSPACE)) + { + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + } + + if (valueHasChanged) *value = TextToInteger(textValue); + + // NOTE: We are not clamp values until user input finishes + //if (*value > maxValue) *value = maxValue; + //else if (*value < minValue) *value = minValue; + + if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + result = 1; + } + } + else + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); + + // Draw cursor rectangle + if (editMode) + { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1, + bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2, + 2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 }; + if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Floating point Value Box control, updates input val_str with numbers +// NOTE: Requires static variables: frameCounter +int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode) +{ + #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) + #define RAYGUI_VALUEBOX_MAX_CHARS 32 + #endif + + int result = 0; + GuiState state = guiState; + + //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; + //snprintf(textValue, sizeof(textValue), "%2.2f", *value); + + Rectangle textBounds = {0}; + if (text != NULL) + { + textBounds.width = (float)GuiGetTextWidth(text) + 2; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + bool valueHasChanged = false; + + if (editMode) + { + state = STATE_PRESSED; + + int keyCount = (int)strlen(textValue); + + // Add or remove minus symbol + if (IsKeyPressed(KEY_MINUS)) + { + if (textValue[0] == '-') + { + for (int i = 0; i < keyCount; i++) + { + textValue[i] = textValue[i + 1]; + } + keyCount--; + valueHasChanged = true; + } + else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS - 1) { + if (keyCount == 0) { + textValue[0] = '0'; + textValue[1] = '\0'; + keyCount++; + } + for (int i = keyCount; i > -1; i--) + { + textValue[i + 1] = textValue[i]; + } + textValue[0] = '-'; + keyCount++; + valueHasChanged = true; + } + } + + // Only allow keys in range [48..57] + if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) + { + if (GuiGetTextWidth(textValue) < bounds.width) + { + int key = GetCharPressed(); + if (((key >= 48) && (key <= 57)) || + (key == '.') || + ((keyCount == 0) && (key == '+')) || // NOTE: Sign can only be in first position + ((keyCount == 0) && (key == '-'))) + { + textValue[keyCount] = (char)key; + keyCount++; + + valueHasChanged = true; + } + } + } + + // Pressed backspace + if (IsKeyPressed(KEY_BACKSPACE)) + { + if (keyCount > 0) + { + keyCount--; + textValue[keyCount] = '\0'; + valueHasChanged = true; + } + } + + if (valueHasChanged) *value = TextToFloat(textValue); + + if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + Color baseColor = BLANK; + if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); + + // Draw cursor + if (editMode) + { + // NOTE: ValueBox internal text is always centered + Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1, + bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, + bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)}; + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); + } + + // Draw text label if provided + GuiDrawText(text, textBounds, + (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, + GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Slider control with pro parameters +// NOTE: Other GuiSlider*() controls use this one +int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + GuiState state = guiState; + + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + float oldValue = *value; + + int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH); + + Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + // Get equivalent value and slider position from mousePosition.x + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + if (!CheckCollisionPointRec(mousePoint, slider)) + { + // Get equivalent value and slider position from mousePosition.x + *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue; + } + } + else state = STATE_FOCUSED; + } + + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + } + + // Control value change check + if (oldValue == *value) result = 0; + else result = 1; + + // Slider bar limits check + float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + if (sliderWidth > 0) // Slider + { + slider.x += sliderValue; + slider.width = (float)sliderWidth; + if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + else if (sliderWidth == 0) // SliderBar + { + slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.width = sliderValue; + if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + + // Draw slider internal bar (depends on state) + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED))); + else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED))); + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textLeft); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textRight); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + //-------------------------------------------------------------------- + + return result; +} + +// Slider Bar control extended, returns selected value +int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 0); + result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue); + GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth); + + return result; +} + +// Progress Bar control extended, shows current progress value +int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) +{ + int result = 0; + GuiState state = guiState; + + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + + // Progress bar + Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), + bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0, + bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 }; + + // Update control + //-------------------------------------------------------------------- + if (*value > maxValue) *value = maxValue; + + // WARNING: Working with floats could lead to rounding issues + if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)); + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK); + } + else + { + if (*value > minValue) + { + // Draw progress bar with colored border, more visual + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + } + else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + + if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + else + { + // Draw borders not yet reached by value + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + } + + // Draw slider internal progress bar (depends on state) + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textLeft); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GuiGetTextWidth(textRight); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); + } + //-------------------------------------------------------------------- + + return result; +} + +// Status Bar control +int GuiStatusBar(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3)))); + //-------------------------------------------------------------------- + + return result; +} + +// Dummy rectangle control, intended for placeholding +int GuiDummyRec(Rectangle bounds, const char *text) +{ + int result = 0; + GuiState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED))); + //------------------------------------------------------------------ + + return result; +} + +// List View control +int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active) +{ + int result = 0; + int itemCount = 0; + const char **items = NULL; + + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); + + result = GuiListViewEx(bounds, items, itemCount, scrollIndex, active, NULL); + + return result; +} + +// List View control with extended parameters +int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus) +{ + int result = 0; + GuiState state = guiState; + + int itemFocused = (focus == NULL)? -1 : *focus; + int itemSelected = (active == NULL)? -1 : *active; + + // Check if we need a scroll bar + bool useScrollBar = false; + if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true; + + // Define base item rectangle [0] + Rectangle itemBounds = { 0 }; + itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING); + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); + if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH); + + // Get items on the list + int visibleItems = (int)bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + if (visibleItems > count) visibleItems = count; + + int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex; + if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0; + int endIndex = startIndex + visibleItems; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + Vector2 mousePoint = GetMousePosition(); + + // Check mouse inside list view + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + + // Check focused and selected item + for (int i = 0; i < visibleItems; i++) + { + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = startIndex + i; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + if (itemSelected == (startIndex + i)) itemSelected = -1; + else itemSelected = startIndex + i; + } + break; + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + } + + if (useScrollBar) + { + int wheelMove = (int)GetMouseWheelMove(); + startIndex -= wheelMove; + + if (startIndex < 0) startIndex = 0; + else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; + + endIndex = startIndex + visibleItems; + if (endIndex > count) endIndex = count; + } + } + else itemFocused = -1; + + // Reset item rectangle y to [0] + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Draw visible items + for (int i = 0; ((i < visibleItems) && (text != NULL)); i++) + { + if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK); + + if (state == STATE_DISABLED) + { + if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); + + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); + } + else + { + if (((startIndex + i) == itemSelected) && (active != NULL)) + { + // Draw item selected + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); + } + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned! + { + // Draw item focused + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); + } + else + { + // Draw item normal (no rectangle) + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); + } + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING)); + } + + if (useScrollBar) + { + Rectangle scrollBarBounds = { + bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Calculate percentage of visible items and apply same percentage to scrollbar + float percentVisible = (float)(endIndex - startIndex)/count; + float sliderSize = bounds.height*percentVisible; + + int prevSliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); // Save default slider size + int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)sliderSize); // Change slider size + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed + + startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems); + + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, prevSliderSize); // Reset slider size to default + } + //-------------------------------------------------------------------- + + if (active != NULL) *active = itemSelected; + if (focus != NULL) *focus = itemFocused; + if (scrollIndex != NULL) *scrollIndex = startIndex; + + return result; +} + +// Color Panel control - Color (RGBA) variant +int GuiColorPanel(Rectangle bounds, const char *text, Color *color) +{ + int result = 0; + + Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f }; + Vector3 hsv = ConvertRGBtoHSV(vcolor); + Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv + + GuiColorPanelHSV(bounds, text, &hsv); + + // Check if the hsv was changed, only then change the color + // This is required, because the Color->HSV->Color conversion has precision errors + // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value + // Otherwise GuiColorPanel would often modify it's color without user input + // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check + if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z) + { + Vector3 rgb = ConvertHSVtoRGB(hsv); + + // NOTE: Vector3ToColor() only available on raylib 1.8.1 + *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), + (unsigned char)(255.0f*rgb.y), + (unsigned char)(255.0f*rgb.z), + color->a }; + } + return result; +} + +// Color Bar Alpha control +// NOTE: Returns alpha value normalized [0..1] +int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +{ + #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE) + #define RAYGUI_COLORBARALPHA_CHECKED_SIZE 10 + #endif + + int result = 0; + GuiState state = guiState; + Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, + (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), + (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), + (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw alpha bar: checked background + if (state != STATE_DISABLED) + { + int checksX = (int)bounds.width/RAYGUI_COLORBARALPHA_CHECKED_SIZE; + int checksY = (int)bounds.height/RAYGUI_COLORBARALPHA_CHECKED_SIZE; + + for (int x = 0; x < checksX; x++) + { + for (int y = 0; y < checksY; y++) + { + Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE }; + GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f)); + } + } + + DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha)); + } + else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + + // Draw alpha bar: selector + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); + //-------------------------------------------------------------------- + + return result; +} + +// Color Bar Hue control +// Returns hue value normalized [0..1] +// NOTE: Other similar bars (for reference): +// Color GuiColorBarSat() [WHITE->color] +// Color GuiColorBarValue() [BLACK->color], HSV/HSL +// float GuiColorBarLuminance() [BLACK->WHITE] +int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) +{ + int result = 0; + GuiState state = guiState; + Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + (*hue)/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + + } + else state = STATE_FOCUSED; + + /*if (IsKeyDown(KEY_UP)) + { + hue -= 2.0f; + if (hue <= 0.0f) hue = 0.0f; + } + else if (IsKeyDown(KEY_DOWN)) + { + hue += 2.0f; + if (hue >= 360.0f) hue = 360.0f; + }*/ + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + // Draw hue bar:color bars + // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha)); + } + else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + + // Draw hue bar: selector + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); + //-------------------------------------------------------------------- + + return result; +} + +// Color Picker control +// NOTE: It's divided in multiple controls: +// Color GuiColorPanel(Rectangle bounds, Color color) +// float GuiColorBarAlpha(Rectangle bounds, float alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanel() size +// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead +int GuiColorPicker(Rectangle bounds, const char *text, Color *color) +{ + int result = 0; + + Color temp = { 200, 0, 0, 255 }; + if (color == NULL) color = &temp; + + GuiColorPanel(bounds, NULL, color); + + Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) }; + + // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used + Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f }); + + GuiColorBarHue(boundsHue, NULL, &hsv.x); + + //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f); + Vector3 rgb = ConvertHSVtoRGB(hsv); + + *color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), (*color).a }; + + return result; +} + +// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering +// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB +// NOTE: It's divided in multiple controls: +// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanelHSV() size +int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + + Vector3 tempHsv = { 0 }; + + if (colorHsv == NULL) + { + const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f }; + tempHsv = ConvertRGBtoHSV(tempColor); + colorHsv = &tempHsv; + } + + GuiColorPanelHSV(bounds, NULL, colorHsv); + + const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + + GuiColorBarHue(boundsHue, NULL, &colorHsv->x); + + return result; +} + +// Color Panel control - HSV variant +int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + GuiState state = guiState; + Vector2 pickerSelector = { 0 }; + + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; + + pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width; // HSV: Saturation + pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height; // HSV: Value + + Vector3 maxHue = { colorHsv->x, 1.0f, 1.0f }; + Vector3 rgbHue = ConvertHSVtoRGB(maxHue); + Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), + (unsigned char)(255.0f*rgbHue.y), + (unsigned char)(255.0f*rgbHue.z), 255 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + pickerSelector = mousePoint; + + if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x; + if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width; + if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y; + if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; + pickerSelector = mousePoint; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha)); + DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0)); + + // Draw color picker: selector + Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; + GuiDrawRectangle(selector, 0, BLANK, colWhite); + } + else + { + DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); + } + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + //-------------------------------------------------------------------- + + return result; +} + +// Message Box control +int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons) +{ + #if !defined(RAYGUI_MESSAGEBOX_BUTTON_HEIGHT) + #define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT 24 + #endif + #if !defined(RAYGUI_MESSAGEBOX_BUTTON_PADDING) + #define RAYGUI_MESSAGEBOX_BUTTON_PADDING 12 + #endif + + int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button + + int buttonCount = 0; + const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; + buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; + + //int textWidth = GuiGetTextWidth(message) + 2; + + Rectangle textBounds = { 0 }; + textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING; + textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2; + textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) result = 0; + + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + + prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonCount; i++) + { + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; + buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); + } + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment); + //-------------------------------------------------------------------- + + return result; +} + +// Text Input Box control, ask for text +int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive) +{ + #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT) + #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT 24 + #endif + #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING) + #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING 12 + #endif + #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT) + #define RAYGUI_TEXTINPUTBOX_HEIGHT 26 + #endif + + // Used to enable text edit mode + // WARNING: No more than one GuiTextInputBox() should be open at the same time + static bool textEditMode = false; + + int result = -1; + + int buttonCount = 0; + const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; + buttonBounds.height = RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT; + + int messageInputHeight = (int)bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - 2*RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + + Rectangle textBounds = { 0 }; + if (message != NULL) + { + int textSize = GuiGetTextWidth(message) + 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.width = (float)textSize; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + } + + Rectangle textBoxBounds = { 0 }; + textBoxBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + textBoxBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_TEXTINPUTBOX_HEIGHT/2; + if (message == NULL) textBoxBounds.y = bounds.y + 24 + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; + else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4); + textBoxBounds.width = bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*2; + textBoxBounds.height = RAYGUI_TEXTINPUTBOX_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) result = 0; + + // Draw message if available + if (message != NULL) + { + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + } + + if (secretViewActive != NULL) + { + static char stars[] = "****************"; + if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height }, + ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode; + + GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", secretViewActive); + } + else + { + if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode; + } + + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonCount; i++) + { + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; + buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); + } + + if (result >= 0) textEditMode = false; + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment); + //-------------------------------------------------------------------- + + return result; // Result is the pressed button index +} + +// Grid control +// NOTE: Returns grid mouse-hover selected cell +// About drawing lines at subpixel spacing, simple put, not easy solution: +// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) +{ + // Grid lines alpha amount + #if !defined(RAYGUI_GRID_ALPHA) + #define RAYGUI_GRID_ALPHA 0.15f + #endif + + int result = 0; + GuiState state = guiState; + + Vector2 mousePoint = GetMousePosition(); + Vector2 currentMouseCell = { -1, -1 }; + + float spaceWidth = spacing/(float)subdivs; + int linesV = (int)(bounds.width/spaceWidth) + 1; + int linesH = (int)(bounds.height/spaceWidth) + 1; + + int color = GuiGetStyle(DEFAULT, LINE_COLOR); + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + // NOTE: Cell values must be the upper left of the cell the mouse is in + currentMouseCell.x = floorf((mousePoint.x - bounds.x)/spacing); + currentMouseCell.y = floorf((mousePoint.y - bounds.y)/spacing); + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == STATE_DISABLED) color = GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED); + + if (subdivs > 0) + { + // Draw vertical grid lines + for (int i = 0; i < linesV; i++) + { + Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 }; + GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); + } + + // Draw horizontal grid lines + for (int i = 0; i < linesH; i++) + { + Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 }; + GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA)); + } + } + + if (mouseCell != NULL) *mouseCell = currentMouseCell; + return result; +} + +//---------------------------------------------------------------------------------- +// Tooltip management functions +// NOTE: Tooltips requires some global variables: tooltipPtr +//---------------------------------------------------------------------------------- +// Enable gui tooltips (global state) +void GuiEnableTooltip(void) { guiTooltip = true; } + +// Disable gui tooltips (global state) +void GuiDisableTooltip(void) { guiTooltip = false; } + +// Set tooltip string +void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; } + +//---------------------------------------------------------------------------------- +// Styles loading functions +//---------------------------------------------------------------------------------- + +// Load raygui style file (.rgs) +// NOTE: By default a binary file is expected, that file could contain a custom font, +// in that case, custom font image atlas is GRAY+ALPHA and pixel data can be compressed (DEFLATE) +void GuiLoadStyle(const char *fileName) +{ + #define MAX_LINE_BUFFER_SIZE 256 + + bool tryBinary = false; + if (!guiStyleLoaded) GuiLoadStyleDefault(); + + // Try reading the files as text file first + FILE *rgsFile = fopen(fileName, "rt"); + + if (rgsFile != NULL) + { + char buffer[MAX_LINE_BUFFER_SIZE] = { 0 }; + fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile); + + if (buffer[0] == '#') + { + int controlId = 0; + int propertyId = 0; + unsigned int propertyValue = 0; + + while (!feof(rgsFile)) + { + switch (buffer[0]) + { + case 'p': + { + // Style property: p + + sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue); + GuiSetStyle(controlId, propertyId, (int)propertyValue); + + } break; + case 'f': + { + // Style font: f + + int fontSize = 0; + char charmapFileName[256] = { 0 }; + char fontFileName[256] = { 0 }; + sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName); + + Font font = { 0 }; + int *codepoints = NULL; + int codepointCount = 0; + + if (charmapFileName[0] != '0') + { + // Load text data from file + // NOTE: Expected an UTF-8 array of codepoints, no separation + char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName)); + codepoints = LoadCodepoints(textData, &codepointCount); + UnloadFileText(textData); + } + + if (fontFileName[0] != '\0') + { + // In case a font is already loaded and it is not default internal font, unload it + if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); + + if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount); + else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); // Default to 95 standard codepoints + } + + // If font texture not properly loaded, revert to default font and size/spacing + if (font.texture.id == 0) + { + font = GetFontDefault(); + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); + } + + UnloadCodepoints(codepoints); + + if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font); + + } break; + default: break; + } + + fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile); + } + } + else tryBinary = true; + + fclose(rgsFile); + } + + if (tryBinary) + { + rgsFile = fopen(fileName, "rb"); + + if (rgsFile != NULL) + { + fseek(rgsFile, 0, SEEK_END); + int fileDataSize = ftell(rgsFile); + fseek(rgsFile, 0, SEEK_SET); + + if (fileDataSize > 0) + { + unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char)); + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); + + GuiLoadStyleFromMemory(fileData, fileDataSize); + + RAYGUI_FREE(fileData); + } + + fclose(rgsFile); + } + } +} + +// Load style default over global style +void GuiLoadStyleDefault(void) +{ + // We set this variable first to avoid cyclic function calls + // when calling GuiSetStyle() and GuiGetStyle() + guiStyleLoaded = true; + + // Initialize default LIGHT style property values + // WARNING: Default value are applied to all controls on set but + // they can be overwritten later on for every custom control + GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); + GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); + GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff); + GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff); + GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff); + GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff); + GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff); + GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff); + GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); + GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); + GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); + GuiSetStyle(DEFAULT, TEXT_PADDING, 0); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + // Initialize default extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15); // DEFAULT, 15 pixels between lines + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds + + // Initialize control-specific property values + // NOTE: Those properties are in default list but require specific values by control type + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 2); + GuiSetStyle(SLIDER, TEXT_PADDING, 4); + GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4); + GuiSetStyle(CHECKBOX, TEXT_PADDING, 4); + GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); + GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0); + GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); + GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(STATUSBAR, TEXT_PADDING, 8); + GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + + // Initialize extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(TOGGLE, GROUP_PADDING, 2); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 16); + GuiSetStyle(SLIDER, SLIDER_PADDING, 1); + GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1); + GuiSetStyle(CHECKBOX, CHECK_PADDING, 1); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); + GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); + GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); + GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24); + GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2); + GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); + GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0); + GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); + GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28); + GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2); + GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1); + GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12); + GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); + GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8); + GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16); + GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); + + if (guiFont.texture.id != GetFontDefault().texture.id) + { + // Unload previous font texture + UnloadTexture(guiFont.texture); + RAYGUI_FREE(guiFont.recs); + RAYGUI_FREE(guiFont.glyphs); + guiFont.recs = NULL; + guiFont.glyphs = NULL; + + // Setup default raylib font + guiFont = GetFontDefault(); + + // NOTE: Default raylib font character 95 is a white square + Rectangle whiteChar = guiFont.recs[95]; + + // 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 }); + } +} + +// Get text with icon id prepended +// NOTE: Useful to add icons by name id (enum) instead of +// a number that can change between ricon versions +const char *GuiIconText(int iconId, const char *text) +{ +#if defined(RAYGUI_NO_ICONS) + return NULL; +#else + static char buffer[1024] = { 0 }; + static char iconBuffer[16] = { 0 }; + + if (text != NULL) + { + memset(buffer, 0, 1024); + snprintf(buffer, 1024, "#%03i#", iconId); + + for (int i = 5; i < 1024; i++) + { + buffer[i] = text[i - 5]; + if (text[i - 5] == '\0') break; + } + + return buffer; + } + else + { + snprintf(iconBuffer, 16, "#%03i#", iconId); + + return iconBuffer; + } +#endif +} + +#if !defined(RAYGUI_NO_ICONS) +// Get full icons data pointer +unsigned int *GuiGetIcons(void) { return guiIconsPtr; } + +// Load raygui icons file (.rgi) +// NOTE: In case nameIds are required, they can be requested with loadIconsName, +// they are returned as a guiIconsName[iconCount][RAYGUI_ICON_MAX_NAME_LENGTH], +// WARNING: guiIconsName[]][] memory should be manually freed! +char **GuiLoadIcons(const char *fileName, bool loadIconsName) +{ + // Style File Structure (.rgi) + // ------------------------------------------------------ + // Offset | Size | Type | Description + // ------------------------------------------------------ + // 0 | 4 | char | Signature: "rGI " + // 4 | 2 | short | Version: 100 + // 6 | 2 | short | reserved + + // 8 | 2 | short | Num icons (N) + // 10 | 2 | short | Icons size (Options: 16, 32, 64) (S) + + // Icons name id (32 bytes per name id) + // foreach (icon) + // { + // 12+32*i | 32 | char | Icon NameId + // } + + // Icons data: One bit per pixel, stored as unsigned int array (depends on icon size) + // S*S pixels/32bit per unsigned int = K unsigned int per icon + // foreach (icon) + // { + // ... | K | unsigned int | Icon Data + // } + + FILE *rgiFile = fopen(fileName, "rb"); + + char **guiIconsName = NULL; + + if (rgiFile != NULL) + { + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + short iconCount = 0; + short iconSize = 0; + + fread(signature, 1, 4, rgiFile); + fread(&version, sizeof(short), 1, rgiFile); + fread(&reserved, sizeof(short), 1, rgiFile); + fread(&iconCount, sizeof(short), 1, rgiFile); + fread(&iconSize, sizeof(short), 1, rgiFile); + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'I') && + (signature[3] == ' ')) + { + if (loadIconsName) + { + guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char **)); + for (int i = 0; i < iconCount; i++) + { + guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH); + fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile); + } + } + else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR); + + // Read icons data directly over internal icons array + fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile); + } + + fclose(rgiFile); + } + + return guiIconsName; +} + +// Load icons from memory +// WARNING: Binary files only +char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName) +{ + unsigned char *fileDataPtr = (unsigned char *)fileData; + char **guiIconsName = NULL; + + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + short iconCount = 0; + short iconSize = 0; + + memcpy(signature, fileDataPtr, 4); + memcpy(&version, fileDataPtr + 4, sizeof(short)); + memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short)); + memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short)); + memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short)); + fileDataPtr += 12; + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'I') && + (signature[3] == ' ')) + { + if (loadIconsName) + { + guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char *)); + for (int i = 0; i < iconCount; i++) + { + guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH); + memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH); + fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH; + } + } + else + { + // Skip icon name data if not required + fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH; + } + + int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int); + guiIconsPtr = (unsigned int *)RAYGUI_MALLOC(iconDataSize); + + memcpy(guiIconsPtr, fileDataPtr, iconDataSize); + } + + return guiIconsName; +} + +// Draw selected icon using rectangles pixel-by-pixel +void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) +{ + #define BIT_CHECK(a,b) ((a) & (1u<<(b))) + + for (int i = 0, y = 0; i < RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32; i++) + { + for (int k = 0; k < 32; k++) + { + if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) + { + #if !defined(RAYGUI_STANDALONE) + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color); + #endif + } + + if ((k == 15) || (k == 31)) y++; + } + } +} + +// Set icon drawing size +void GuiSetIconScale(int scale) +{ + if (scale >= 1) guiIconScale = scale; +} + +// Get text width considering gui style and icon size (if required) +int GuiGetTextWidth(const char *text) +{ + #if !defined(ICON_TEXT_PADDING) + #define ICON_TEXT_PADDING 4 + #endif + + Vector2 textSize = { 0 }; + int textIconOffset = 0; + + if ((text != NULL) && (text[0] != '\0')) + { + if (text[0] == '#') + { + for (int i = 1; (i < 5) && (text[i] != '\0'); i++) + { + if (text[i] == '#') + { + textIconOffset = i; + break; + } + } + } + + text += textIconOffset; + + // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly + float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + + // Custom MeasureText() implementation + if ((guiFont.texture.id > 0) && (text != NULL)) + { + // Get size in bytes of text, considering end of line and line break + int size = 0; + for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++) + { + if ((text[i] != '\0') && (text[i] != '\n')) size++; + else break; + } + + float scaleFactor = fontSize/(float)guiFont.baseSize; + textSize.y = (float)guiFont.baseSize*scaleFactor; + float glyphWidth = 0.0f; + + for (int i = 0, codepointSize = 0; i < size; i += codepointSize) + { + int codepoint = GetCodepointNext(&text[i], &codepointSize); + int codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + } + + if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING); + } + + return (int)textSize.x; +} + +#endif // !RAYGUI_NO_ICONS + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// Load style from memory +// WARNING: Binary files 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 j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (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 + + 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 + Rectangle fontWhiteRec = { 0 }; + memcpy(&fontWhiteRec, 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 > 0) && (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); + + RAYGUI_FREE(imFont.data); + + // Validate font atlas texture was loaded correctly + if (font.texture.id != 0) + { + // Load font recs data + int recsDataSize = font.glyphCount*sizeof(Rectangle); + int recsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed recs data + memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize)) + { + // Recs data is compressed, uncompress it + unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize); + + memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize); + fileDataPtr += recsDataCompressedSize; + + int recsDataUncompSize = 0; + font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted"); + + RAYGUI_FREE(recsDataCompressed); + } + else + { + // Recs data is uncompressed + 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 glyphs info data + int glyphsDataSize = font.glyphCount*16; // 16 bytes data per glyph + int glyphsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed glyphs data + memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + // Allocate required glyphs space to fill with data + font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); + + if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize)) + { + // Glyphs data is compressed, uncompress it + unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize); + + memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize); + fileDataPtr += glyphsDataCompressedSize; + + int glyphsDataUncompSize = 0; + unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted"); + + unsigned char *glyphsDataUncompPtr = glyphsDataUncomp; + + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int)); + glyphsDataUncompPtr += 16; + } + + RAYGUI_FREE(glypsDataCompressed); + RAYGUI_FREE(glyphsDataUncomp); + } + else + { + // Glyphs data is uncompressed + 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; + } + } + } + else font = GetFontDefault(); // Fallback in case of errors loading font atlas texture + + GuiSetFont(font); + + // Set font texture source rectangle to be used as white texture to draw shapes + // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call + if ((fontWhiteRec.x > 0) && + (fontWhiteRec.y > 0) && + (fontWhiteRec.width > 0) && + (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec); + } +#endif + } +} + +// Get text bounds considering control bounds +static Rectangle GetTextBounds(int control, Rectangle bounds) +{ + Rectangle textBounds = bounds; + + textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); + textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING); + textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); // NOTE: Text is processed line per line! + + // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds + switch (control) + { + case COMBOBOX: + case DROPDOWNBOX: + case LISTVIEW: + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW + case SLIDER: + case CHECKBOX: + case VALUEBOX: + case CONTROL11: + // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER + default: + { + // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText() + if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); + else textBounds.x += GuiGetStyle(control, TEXT_PADDING); + } + break; + } + + return textBounds; +} + +// Get text icon if provided and move text cursor +// NOTE: We support up to 999 values for iconId +static const char *GetTextIcon(const char *text, int *iconId) +{ +#if !defined(RAYGUI_NO_ICONS) + *iconId = -1; + if (text[0] == '#') // Maybe we have an icon! + { + char iconValue[4] = { 0 }; // Maximum length for icon value: 3 digits + '\0' + + int pos = 1; + while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9')) + { + iconValue[pos - 1] = text[pos]; + pos++; + } + + if (text[pos] == '#') + { + *iconId = TextToInteger(iconValue); + + // Move text pointer after icon + // WARNING: If only icon provided, it could point to EOL character: '\0' + if (*iconId >= 0) text += (pos + 1); + } + } +#endif + + return text; +} + +// Get text divided into lines (by line-breaks '\n') +// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator! +static const char **GetTextLines(const char *text, int *count) +{ + #define RAYGUI_MAX_TEXT_LINES 128 + + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings + + int textSize = (int)strlen(text); + + lines[0] = text; + int len = 0; + *count = 1; + //int lineSize = 0; // Stores current line size, not returned + + for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + { + if (text[i] == '\n') + { + //lineSize = len; + k++; + lines[k] = &text[i + 1]; // WARNING: next value is valid? + len = 0; + *count += 1; + } + else len++; + } + + //lines[*count - 1].size = len; + + return lines; +} + +// Get text width to next space for provided string +static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex) +{ + float width = 0; + int codepointByteCount = 0; + int codepoint = 0; + int index = 0; + float glyphWidth = 0; + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + for (int i = 0; text[i] != '\0'; i++) + { + if (text[i] != ' ') + { + codepoint = GetCodepoint(&text[i], &codepointByteCount); + index = GetGlyphIndex(guiFont, codepoint); + glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor; + width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + else + { + *nextSpaceIndex = i; + break; + } + } + + return width; +} + +// Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint) +{ + #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect + + #if !defined(ICON_TEXT_PADDING) + #define ICON_TEXT_PADDING 4 + #endif + + if ((text == NULL) || (text[0] == '\0')) return; // Security check + + // PROCEDURE: + // - Text is processed line per line + // - For every line, horizontal alignment is defined + // - For all text, vertical alignment is defined (multiline text only) + // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) + + // Get text lines (using '\n' as delimiter) to be processed individually + // WARNING: We can't use GuiTextSplit() function because it can be already used + // before the GuiDrawText() call and its buffer is static, it would be overriden :( + int lineCount = 0; + const char **lines = GetTextLines(text, &lineCount); + + // Text style variables + //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing + + // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float posOffsetY = 0.0f; + + for (int i = 0; i < lineCount; i++) + { + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor + + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; + float textBoundsWidthOffset = 0.0f; + + // NOTE: We get text size after icon has been processed + // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed? + int textSizeX = GuiGetTextWidth(lines[i]); + + // If text requires an icon, add size to measure + if (iconId >= 0) + { + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; + + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' +#if !defined(RAYGUI_NO_ICONS) + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; +#endif + } + + // Check guiTextAlign global variables + switch (alignment) + { + case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break; + case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x + textBounds.width/2 - textSizeX/2; break; + case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break; + default: break; + } + + if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x; + + switch (alignmentVertical) + { + // Only valid in case of wordWrap = 0; + case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break; + case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + default: break; + } + + // NOTE: Make sure we get pixel-perfect coordinates, + // In case of decimals we got weird text positioning + textBoundsPosition.x = (float)((int)textBoundsPosition.x); + textBoundsPosition.y = (float)((int)textBoundsPosition.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if !defined(RAYGUI_NO_ICONS) + if (iconId >= 0) + { + // NOTE: We consider icon height, probably different than text size + GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); + textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + } +#endif + // Get size in bytes of text, + // considering end of line and line break + int lineSize = 0; + for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ } + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + int lastSpaceIndex = 0; + bool tempWrapCharMode = false; + + int textOffsetY = 0; + float textOffsetX = 0.0f; + float glyphWidth = 0; + + int ellipsisWidth = GuiGetTextWidth("..."); + bool textOverflow = false; + for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize) + { + int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); + int index = GetGlyphIndex(guiFont, 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) codepointSize = 1; // TODO: Review not recognized codepoints size + + // Get glyph width to check if it goes out of bounds + if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor); + else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor; + + // Wrap mode text measuring, to validate if + // it can be drawn or a new line is required + if (wrapMode == TEXT_WRAP_CHAR) + { + // Jump to next line if current character reach end of the box limits + if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset) + { + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + + if (tempWrapCharMode) // Wrap at char level when too long words + { + wrapMode = TEXT_WRAP_WORD; + tempWrapCharMode = false; + } + } + } + else if (wrapMode == TEXT_WRAP_WORD) + { + if (codepoint == 32) lastSpaceIndex = c; + + // Get width to next space in line + int nextSpaceIndex = 0; + float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex); + + int nextSpaceIndex2 = 0; + float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2); + + if (nextWordSize > textBounds.width - textBoundsWidthOffset) + { + // Considering the case the next word is longer than bounds + tempWrapCharMode = true; + wrapMode = TEXT_WRAP_CHAR; + } + else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset) + { + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + } + } + + if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint + else + { + // TODO: There are multiple types of spaces in Unicode, + // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html + if ((codepoint != ' ') && (codepoint != '\t')) // Do not draw codepoints with no glyph + { + if (wrapMode == TEXT_WRAP_NONE) + { + // Draw only required text glyphs fitting the textBounds.width + if (textSizeX > textBounds.width) + { + if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + else if (!textOverflow) + { + textOverflow = true; + + for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3) + { + DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + else + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) + { + // Draw only glyphs inside the bounds + if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + + if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + } + + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + //--------------------------------------------------------------------------------- + } + +#if defined(RAYGUI_DEBUG_TEXT_BOUNDS) + GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f)); +#endif +} + +// Gui draw rectangle using default raygui plain style with borders +static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color) +{ + if (color.a > 0) + { + // Draw rectangle filled with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha)); + } + + if (borderWidth > 0) + { + // Draw rectangle border lines with color + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + } + +#if defined(RAYGUI_DEBUG_RECS_BOUNDS) + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f)); +#endif +} + +// Draw tooltip using control bounds +static void GuiTooltip(Rectangle controlRec) +{ + if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode) + { + Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + + if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width); + + GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL); + + int textPadding = GuiGetStyle(LABEL, TEXT_PADDING); + int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_PADDING, 0); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment); + GuiSetStyle(LABEL, TEXT_PADDING, textPadding); + } +} + +// Split controls text into multiple strings +// Also check for multiple columns (required by GuiToggleGroup()) +static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) +{ + // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) + // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, + // all used memory is static... it has some limitations: + // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS + // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE + // NOTE: Those definitions could be externally provided if required + + // 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 + + #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #endif + #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) + #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #endif + + static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) + memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); + + result[0] = buffer; + int counter = 1; + + if (textRow != NULL) textRow[0] = 0; + + // Count how many substrings we have on text and point to every one + for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if ((buffer[i] == delimiter) || (buffer[i] == '\n')) + { + result[counter] = buffer + i + 1; + + if (textRow != NULL) + { + if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1; + else textRow[counter] = textRow[counter - 1]; + } + + buffer[i] = '\0'; // Set an end of string at this point + + counter++; + if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break; + } + } + + *count = counter; + + return result; +} + +// Convert color data from RGB to HSV +// NOTE: Color data should be passed normalized +static Vector3 ConvertRGBtoHSV(Vector3 rgb) +{ + Vector3 hsv = { 0 }; + float min = 0.0f; + float max = 0.0f; + float delta = 0.0f; + + min = (rgb.x < rgb.y)? rgb.x : rgb.y; + min = (min < rgb.z)? min : rgb.z; + + max = (rgb.x > rgb.y)? rgb.x : rgb.y; + max = (max > rgb.z)? max : rgb.z; + + hsv.z = max; // Value + delta = max - min; + + if (delta < 0.00001f) + { + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + if (max > 0.0f) + { + // NOTE: If max is 0, this divide would cause a crash + hsv.y = (delta/max); // Saturation + } + else + { + // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + // NOTE: Comparing float values could not work properly + if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta + else + { + if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow + else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan + } + + hsv.x *= 60.0f; // Convert to degrees + + if (hsv.x < 0.0f) hsv.x += 360.0f; + + return hsv; +} + +// Convert color data from HSV to RGB +// NOTE: Color data should be passed normalized +static Vector3 ConvertHSVtoRGB(Vector3 hsv) +{ + Vector3 rgb = { 0 }; + float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f; + long i = 0; + + // NOTE: Comparing float values could not work properly + if (hsv.y <= 0.0f) + { + rgb.x = hsv.z; + rgb.y = hsv.z; + rgb.z = hsv.z; + return rgb; + } + + hh = hsv.x; + if (hh >= 360.0f) hh = 0.0f; + hh /= 60.0f; + + i = (long)hh; + ff = hh - i; + p = hsv.z*(1.0f - hsv.y); + q = hsv.z*(1.0f - (hsv.y*ff)); + t = hsv.z*(1.0f - (hsv.y*(1.0f - ff))); + + switch (i) + { + case 0: + { + rgb.x = hsv.z; + rgb.y = t; + rgb.z = p; + } break; + case 1: + { + rgb.x = q; + rgb.y = hsv.z; + rgb.z = p; + } break; + case 2: + { + rgb.x = p; + rgb.y = hsv.z; + rgb.z = t; + } break; + case 3: + { + rgb.x = p; + rgb.y = q; + rgb.z = hsv.z; + } break; + case 4: + { + rgb.x = t; + rgb.y = p; + rgb.z = hsv.z; + } break; + case 5: + default: + { + rgb.x = hsv.z; + rgb.y = p; + rgb.z = q; + } break; + } + + return rgb; +} + +// Scroll bar control (used by GuiScrollPanel()) +static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) +{ + GuiState state = guiState; + + // Is the scrollbar horizontal or vertical? + bool isVertical = (bounds.width > bounds.height)? false : true; + + // The size (width or height depending on scrollbar type) of the spinner buttons + const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)? + (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : + (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0; + + // Arrow buttons [<] [>] [∧] [∨] + Rectangle arrowUpLeft = { 0 }; + Rectangle arrowDownRight = { 0 }; + + // Actual area of the scrollbar excluding the arrow buttons + Rectangle scrollbar = { 0 }; + + // Slider bar that moves --[///]----- + Rectangle slider = { 0 }; + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + + int valueRange = maxValue - minValue; + if (valueRange <= 0) valueRange = 1; + + int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); + if (sliderSize < 1) sliderSize = 1; // TODO: Consider a minimum slider size + + // Calculate rectangles for all of the components + arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)spinnerSize, (float)spinnerSize }; + + if (isVertical) + { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + scrollbar.y + (int)(((float)(value - minValue)/valueRange)*(scrollbar.height - sliderSize)), + bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), + (float)sliderSize }; + } + else // horizontal + { + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + scrollbar.x + (int)(((float)(value - minValue)/valueRange)*(scrollbar.width - sliderSize)), + bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + (float)sliderSize, + bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) }; + } + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + !CheckCollisionPointRec(mousePoint, arrowUpLeft) && + !CheckCollisionPointRec(mousePoint, arrowDownRight)) + { + if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec)) + { + state = STATE_PRESSED; + + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); + } + } + else + { + guiControlExclusiveMode = false; + guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = STATE_FOCUSED; + + // Handle mouse wheel + int wheel = (int)GetMouseWheelMove(); + if (wheel != 0) value += wheel; + + // Handle mouse button down + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + guiControlExclusiveMode = true; + guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts + + // Check arrows click + if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (!CheckCollisionPointRec(mousePoint, slider)) + { + // If click on scrollbar position but not on slider, place slider directly on that position + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); + } + + state = STATE_PRESSED; + } + + // Keyboard control on mouse hover scrollbar + /* + if (isVertical) + { + if (IsKeyDown(KEY_DOWN)) value += 5; + else if (IsKeyDown(KEY_UP)) value -= 5; + } + else + { + if (IsKeyDown(KEY_RIGHT)) value += 5; + else if (IsKeyDown(KEY_LEFT)) value -= 5; + } + */ + } + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED))); // Draw the background + + GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL))); // Draw the scrollbar active area background + GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3))); // Draw the slider bar + + // Draw arrows (using icon if available) + if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) + { +#if defined(RAYGUI_NO_ICONS) + GuiDrawText(isVertical? "^" : "<", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); + GuiDrawText(isVertical? "v" : ">", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); +#else + GuiDrawText(isVertical? "#121#" : "#118#", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL + GuiDrawText(isVertical? "#120#" : "#119#", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL +#endif + } + //-------------------------------------------------------------------- + + return value; +} + +// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +// WARNING: It multiplies current alpha by alpha scale factor +static Color GuiFade(Color color, float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) }; + + return result; +} + +#if defined(RAYGUI_STANDALONE) +// Returns a Color struct from hexadecimal value +static Color GetColor(int hexValue) +{ + Color color; + + color.r = (unsigned char)(hexValue >> 24) & 0xff; + color.g = (unsigned char)(hexValue >> 16) & 0xff; + color.b = (unsigned char)(hexValue >> 8) & 0xff; + color.a = (unsigned char)hexValue & 0xff; + + return color; +} + +// Returns hexadecimal value for a Color +static int ColorToInt(Color color) +{ + return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a); +} + +// Check if point is inside rectangle +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) +{ + bool collision = false; + + if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && + (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true; + + return collision; +} + +// Formatting of text with variables to 'embed' +static const char *TextFormat(const char *text, ...) +{ + #if !defined(RAYGUI_TEXTFORMAT_MAX_SIZE) + #define RAYGUI_TEXTFORMAT_MAX_SIZE 256 + #endif + + static char buffer[RAYGUI_TEXTFORMAT_MAX_SIZE]; + + va_list args; + va_start(args, text); + vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args); + va_end(args); + + return buffer; +} + +// Draw rectangle with vertical gradient fill color +// NOTE: This function is only used by GuiColorPicker() +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2) +{ + Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height }; + DrawRectangleGradientEx(bounds, color1, color2, color2, color1); +} + +// Split string into multiple strings +const char **TextSplit(const char *text, char delimiter, int *count) +{ + // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) + // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, + // all used memory is static... it has some limitations: + // 1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS + // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE + + #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #endif + #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) + #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #endif + + static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; + memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); + + result[0] = buffer; + int counter = 0; + + if (text != NULL) + { + counter = 1; + + // Count how many substrings we have on text and point to every one + for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if (buffer[i] == delimiter) + { + buffer[i] = '\0'; // Set an end of string at this point + result[counter] = buffer + i + 1; + counter++; + + if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break; + } + } + } + + *count = counter; + return result; +} + +// Get integer value from text +// NOTE: This function replaces atoi() [stdlib.h] +static int TextToInteger(const char *text) +{ + int value = 0; + int sign = 1; + + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1; + text++; + } + + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0'); + + return value*sign; +} + +// Get float value from text +// NOTE: This function replaces atof() [stdlib.h] +// WARNING: Only '.' character is understood as decimal point +static float TextToFloat(const char *text) +{ + float value = 0.0f; + float sign = 1.0f; + + if ((text[0] == '+') || (text[0] == '-')) + { + if (text[0] == '-') sign = -1.0f; + text++; + } + + int i = 0; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0'); + + if (text[i++] != '.') value *= sign; + else + { + float divisor = 10.0f; + for (; ((text[i] >= '0') && (text[i] <= '9')); i++) + { + value += ((float)(text[i] - '0'))/divisor; + divisor = divisor*10.0f; + } + } + + return value; +} + +// Encode codepoint into UTF-8 text (char array size returned as parameter) +static const char *CodepointToUTF8(int codepoint, int *byteSize) +{ + static char utf8[6] = { 0 }; + int size = 0; + + if (codepoint <= 0x7f) + { + utf8[0] = (char)codepoint; + size = 1; + } + else if (codepoint <= 0x7ff) + { + utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); + utf8[1] = (char)((codepoint & 0x3f) | 0x80); + size = 2; + } + else if (codepoint <= 0xffff) + { + utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0); + utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[2] = (char)((codepoint & 0x3f) | 0x80); + size = 3; + } + else if (codepoint <= 0x10ffff) + { + utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0); + utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80); + utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80); + utf8[3] = (char)((codepoint & 0x3f) | 0x80); + size = 4; + } + + *byteSize = size; + + return utf8; +} + +// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found +// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned +// Total number of bytes processed are returned as a parameter +// NOTE: the standard says U+FFFD should be returned in case of errors +// but that character is not supported by the default font in raylib +static int GetCodepointNext(const char *text, int *codepointSize) +{ + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + *codepointSize = 1; + + // Get current codepoint and bytes processed + if (0xf0 == (0xf8 & ptr[0])) + { + // 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]); + *codepointSize = 4; + } + else if (0xe0 == (0xf0 & ptr[0])) + { + // 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]); + *codepointSize = 3; + } + else if (0xc0 == (0xe0 & ptr[0])) + { + // 2 byte UTF-8 codepoint + if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks + codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); + *codepointSize = 2; + } + else if (0x00 == (0x80 & ptr[0])) + { + // 1 byte UTF-8 codepoint + codepoint = ptr[0]; + *codepointSize = 1; + } + + return codepoint; +} +#endif // RAYGUI_STANDALONE + +#endif // RAYGUI_IMPLEMENTATION diff --git a/src/raygui_impl.c b/src/raygui_impl.c new file mode 100644 index 0000000..5be219f --- /dev/null +++ b/src/raygui_impl.c @@ -0,0 +1,2 @@ +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" \ No newline at end of file