Mercurial
view third_party/raylib/include/raygui.h @ 71:75de5903355c
Giagantic changes that update Dowa library to be more align with stb style array and hashmap. Updated Seobeo to be caching on server side instead of file level caching. Deleted bunch of things I don't really use.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sun, 28 Dec 2025 20:34:22 -0800 |
| parents | b3e82d22f961 |
| children | 48f260576059 |
line wrap: on
line source
/******************************************************************************************* * * 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 int = 384*4 bytes = 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 (xx-Nov-2025) ADDED: Support up to 32 controls (v500) * 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.6-dev - 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_CIRCLE_ADD = 234, ICON_CIRCLE_ADD_FILL = 235, ICON_CIRCLE_WARNING = 236, ICON_CIRCLE_WARNING_FILL = 237, ICON_BOX_MORE = 238, ICON_BOX_MORE_FILL = 239, ICON_BOX_MINUS = 240, ICON_BOX_MINUS_FILL = 241, ICON_UNION = 242, ICON_INTERSECTION = 243, ICON_DIFFERENCE = 244, ICON_SPHERE = 245, ICON_CYLINDER = 246, ICON_CONE = 247, ICON_ELLIPSOID = 248, ICON_CAPSULE = 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 <ctype.h> // required for: isspace() [GuiTextBox()] #include <stdio.h> // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()] #include <string.h> // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy() #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()] #include <math.h> // 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 <stdlib.h> // 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) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)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 0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_ADD 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_ADD_FILL 0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0, // ICON_CIRCLE_WARNING 0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0, // ICON_CIRCLE_WARNING_FILL 0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MORE 0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MORE_FILL 0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS 0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000, // ICON_BOX_MINUS_FILL 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0, // ICON_UNION 0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_INTERSECTION 0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_DIFFERENCE 0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0, // ICON_SPHERE 0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0, // ICON_CYLINDER 0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0, // ICON_CONE 0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000, // ICON_ELLIPSOID 0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000, // ICON_CAPSULE 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 //---------------------------------------------------------------------------------- // Module 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 Internal 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 148 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, text[i], &toggle); } else { toggle = false; GuiToggle(tabBounds, 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 20 // Frames to wait for autocursor movement #endif #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement #endif int result = 0; GuiState state = guiState; bool multiline = true; // 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; // Calculate cursor position for multiline int cursorLine = 0; // Current line number (0-based) int lineStart = 0; // Start index of current line if (multiline) { // Count newlines before cursor to determine line number for (int i = 0; i < thisCursorIndex; i++) { if (text[i] == '\n') { cursorLine++; lineStart = i + 1; } } } // Calculate horizontal position within current line char lineText[1024] = { 0 }; int lineTextLen = 0; if (multiline) { // Extract current line text up to cursor int i = lineStart; while (i < thisCursorIndex && text[i] != '\n' && lineTextLen < 1023) { lineText[lineTextLen++] = text[i++]; } lineText[lineTextLen] = '\0'; } int textWidth = multiline ? GuiGetTextWidth(lineText) : (GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex)); int textIndexOffset = 0; // Text index offset to start drawing in the box // Line height for multiline (matches GuiDrawText line spacing) int lineHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); // Cursor rectangle // NOTE: Position X and Y values updated for multiline support Rectangle cursor = { textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), multiline ? (textBounds.y + cursorLine * lineHeight) : (textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)), 2, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }; 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 text 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'; } } } else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize)) { // Adding codepoint to text, at current cursor position // 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; } else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, after current cursor position 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 = 0; int prevCodepoint = 0; // 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; } else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger))) { // Delete single codepoint from text, before current cursor position 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 = 0; int prevCodepoint = 0; // 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; } // Vertical cursor movement for multiline if (multiline && (IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger))) { // Find start of current line int currentLineStart = textBoxCursorIndex; while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; // Calculate horizontal position in current line int horizontalPos = textBoxCursorIndex - currentLineStart; // Find start of previous line if (currentLineStart > 0) { int prevLineEnd = currentLineStart - 1; // Skip the newline int prevLineStart = prevLineEnd; while (prevLineStart > 0 && text[prevLineStart - 1] != '\n') prevLineStart--; // Move to same horizontal position on previous line (or end of line if shorter) int prevLineLength = prevLineEnd - prevLineStart; int targetPos = (horizontalPos < prevLineLength) ? horizontalPos : prevLineLength; textBoxCursorIndex = prevLineStart + targetPos; } else { // Already on first line, move to start textBoxCursorIndex = 0; } } else if (multiline && (IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger))) { // Find start of current line int currentLineStart = textBoxCursorIndex; while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--; // Calculate horizontal position in current line int horizontalPos = textBoxCursorIndex - currentLineStart; // Find end of current line int currentLineEnd = textBoxCursorIndex; while (currentLineEnd < textLength && text[currentLineEnd] != '\n') currentLineEnd++; // Find next line if (currentLineEnd < textLength) { int nextLineStart = currentLineEnd + 1; // Skip the newline int nextLineEnd = nextLineStart; while (nextLineEnd < textLength && text[nextLineEnd] != '\n') nextLineEnd++; // Move to same horizontal position on next line (or end of line if shorter) int nextLineLength = nextLineEnd - nextLineStart; int targetPos = (horizontalPos < nextLineLength) ? horizontalPos : nextLineLength; textBoxCursorIndex = nextLineStart + targetPos; } else { // Already on last line, move to end textBoxCursorIndex = textLength; } } // 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 for multiline if (multiline) { // Recalculate cursor line and position int newCursorLine = 0; int newLineStart = 0; for (int i = 0; i < textBoxCursorIndex; i++) { if (text[i] == '\n') { newCursorLine++; newLineStart = i + 1; } } // Extract current line text up to cursor char currentLineText[1024] = { 0 }; int currentLineLen = 0; int i = newLineStart; while (i < textBoxCursorIndex && text[i] != '\n' && currentLineLen < 1023) { currentLineText[currentLineLen++] = text[i++]; } currentLineText[currentLineLen] = '\0'; cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(currentLineText) + GuiGetStyle(DEFAULT, TEXT_SPACING); cursor.y = textBounds.y + (newCursorLine * lineHeight * 1.5); } else { cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); } // 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 // Set vertical alignment to top for multiline int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); // Restore previous vertical alignment if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment); // 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) { 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; } } // Add new digit to text value if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width)) { int key = GetCharPressed(); // Only allow keys in range [48..57] if ((key >= 48) && (key <= 57)) { textValue[keyCount] = (char)key; keyCount++; valueHasChanged = true; } } // Delete text if ((keyCount > 0) && 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); } int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); 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; } GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment); 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: // Ref: 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 <control_id> <property_id> <property_value> <property_name> sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue); GuiSetStyle(controlId, propertyId, (int)propertyValue); } break; case 'f': { // Style font: f <gen_font_size> <charmap_file> <font_file> 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_CALLOC(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_CALLOC(iconCount, sizeof(char *)); for (int i = 0; i < iconCount; i++) { guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char)); 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_CALLOC(iconCount, sizeof(char *)); for (int i = 0; i < iconCount; i++) { guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char)); 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_CALLOC(iconDataSize, 1); 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 Internal 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_CALLOC(fontImageCompSize, sizeof(unsigned char)); 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_CALLOC(fontImageUncompSize, sizeof(unsigned char)); 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_CALLOC(recsDataCompressedSize, sizeof(unsigned char)); 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_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char)); 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 textLength = (int)strlen(text); lines[0] = text; *count = 1; for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { if (text[i] == '\n') { k++; lines[k] = &text[i + 1]; // WARNING: next value is valid? *count += 1; } } 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