From b68e522ffc39ebff8268c6b522a3bb965878ad16 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 24 Dec 2020 13:51:24 +0100 Subject: [PATCH] Updated several files while porting examples to web --- examples/Makefile | 1 + examples/core/core_window_flags.png | Bin 0 -> 22040 bytes examples/models/rlights.h | 20 +- examples/shaders/rlights.h | 7 +- examples/shapes/raygui.h | 3878 +++++++---------- examples/shapes/ricons.h | 556 +++ .../shapes/shapes_draw_rectangle_rounded.c | 14 +- examples/text/text_font_sdf.c | 2 +- examples/textures/textures_draw_tiled.png | Bin 0 -> 49779 bytes 9 files changed, 2110 insertions(+), 2368 deletions(-) create mode 100644 examples/core/core_window_flags.png create mode 100644 examples/shapes/ricons.h create mode 100644 examples/textures/textures_draw_tiled.png diff --git a/examples/Makefile b/examples/Makefile index 18c7b05f8..9c9e4b9fd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -487,6 +487,7 @@ SHADERS = \ shaders/shaders_fog \ shaders/shaders_simple_mask \ shaders/shaders_spotlight \ + shaders/shaders_hot_reloading \ shaders/shaders_rlgl_mesh_instanced \ shaders/shaders_multi_sample2d diff --git a/examples/core/core_window_flags.png b/examples/core/core_window_flags.png new file mode 100644 index 0000000000000000000000000000000000000000..413d2a86bf648fc91c402cd8800a111fb48583d2 GIT binary patch literal 22040 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 ListElement +* - ListView * - ColorPicker --> ColorPanel, ColorBarHue -* - MessageBox --> Label, Button -* - TextInputBox --> Label, TextBox, Button +* - 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). * @@ -63,15 +63,20 @@ * internally in the library and input management and drawing functions must be provided by * the user (check library implementation for further details). * -* #define RAYGUI_RICONS_SUPPORT -* Includes ricons.h header defining a set of 128 icons (binary format) to be used on +* #define RAYGUI_SUPPORT_ICONS +* Includes riconsdata.h header defining a set of 128 icons (binary format) to be used on * multiple controls and following raygui styles * -* #define RAYGUI_TEXTBOX_EXTENDED -* Enables the advance GuiTextBox()/GuiValueBox()/GuiSpinner() implementation with -* text selection support and text copy/cut/paste support * * VERSIONS HISTORY: +* 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 @@ -103,7 +108,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2020 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. @@ -125,39 +130,57 @@ #ifndef RAYGUI_H #define RAYGUI_H -#define RAYGUI_VERSION "2.5-dev" +#define RAYGUI_VERSION "2.6-dev" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" #endif -#if defined(RAYGUI_IMPLEMENTATION) - #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) - #define RAYGUIDEF __declspec(dllexport) extern // We are building raygui as a Win32 shared library (.dll). - #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) - #define RAYGUIDEF __declspec(dllimport) // We are using raygui as a Win32 shared library (.dll) +// Define functions scope to be used internally (static) or externally (extern) to the module including this file +#if defined(RAYGUI_STATIC) + #define RAYGUIDEF static // Functions just visible to module including this file +#else + #ifdef __cplusplus + #define RAYGUIDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) #else - #ifdef __cplusplus - #define RAYGUIDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) - #else - #define RAYGUIDEF extern // Functions visible from other files - #endif + // NOTE: By default any function declared in a C file is extern + #define RAYGUIDEF extern // Functions visible from other files #endif -#elif defined(RAYGUI_STATIC) - #define RAYGUIDEF static // Functions just visible to module including this file #endif -#include // Required for: atoi() +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RAYGUIDEF __declspec(dllexport) // We are building raygui as a Win32 shared library (.dll). + #elif defined(USE_LIBTYPE_SHARED) + #define RAYGUIDEF __declspec(dllimport) // We are using raygui as a Win32 shared library (.dll) + #endif +#endif + + +#if !defined(RAYGUI_MALLOC) && !defined(RAYGUI_CALLOC) && !defined(RAYGUI_FREE) + #include // Required for: malloc(), calloc(), free() +#endif + +// Allow custom memory allocators +#ifndef RAYGUI_MALLOC + #define RAYGUI_MALLOC(sz) malloc(sz) +#endif +#ifndef RAYGUI_CALLOC + #define RAYGUI_CALLOC(n,sz) calloc(n,sz) +#endif +#ifndef RAYGUI_FREE + #define RAYGUI_FREE(p) free(p) +#endif //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define TEXTEDIT_CURSOR_BLINK_FRAMES 20 // Text edit controls cursor blink timming - #define NUM_CONTROLS 16 // Number of standard controls #define NUM_PROPS_DEFAULT 16 // Number of standard properties #define NUM_PROPS_EXTENDED 8 // Number of extended properties +#define TEXTEDIT_CURSOR_BLINK_FRAMES 20 // Text edit controls cursor blink timming + //---------------------------------------------------------------------------------- // Types and Structures Definition // NOTE: Some types are required for RAYGUI_STANDALONE usage @@ -193,30 +216,42 @@ // Rectangle type typedef struct Rectangle { - int x; - int y; - int width; - int height; + float x; + float y; + float width; + float height; } Rectangle; - // Texture2D type - // NOTE: It should be provided by user - typedef struct Texture2D Texture2D; + // TODO: Texture2D type is very coupled to raylib, mostly required by GuiImageButton() + // It should be redesigned to be provided by user + typedef struct Texture2D { + 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) + } Texture2D; - // Font type - // NOTE: It should be provided by user - typedef struct Font Font; + // Font character info + typedef struct CharInfo CharInfo; + + // 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 charsCount; // Number of characters + Texture2D texture; // Characters texture atlas + Rectangle *recs; // Characters rectangles in texture + CharInfo *chars; // Characters info data + } Font; #endif -#if defined(RAYGUI_TEXTBOX_EXTENDED) -// Gui text box state data -typedef struct GuiTextBoxState { - int cursor; // Cursor position in text - int start; // Text start position (from where we begin drawing the text) - int index; // Text start index (index inside the text of `start` always in sync) - int select; // Marks position of cursor when selection has started -} GuiTextBoxState; -#endif +// Style property +typedef struct GuiStyleProp { + unsigned short controlId; + unsigned short propertyId; + int propertyValue; +} GuiStyleProp; // Gui control state typedef enum { @@ -250,7 +285,7 @@ typedef enum { LISTVIEW, COLORPICKER, SCROLLBAR, - RESERVED + STATUSBAR } GuiControl; // Gui base properties for every control @@ -268,9 +303,9 @@ typedef enum { BASE_COLOR_DISABLED, TEXT_COLOR_DISABLED, BORDER_WIDTH, - INNER_PADDING, + TEXT_PADDING, TEXT_ALIGNMENT, - RESERVED02 + RESERVED } GuiControlProperty; // Gui extended properties depend on control @@ -298,48 +333,53 @@ typedef enum { // Slider / SliderBar typedef enum { SLIDER_WIDTH = 16, - TEXT_PADDING + SLIDER_PADDING } GuiSliderProperty; // ProgressBar -//typedef enum { } GuiProgressBarProperty; +typedef enum { + PROGRESS_PADDING = 16, +} GuiProgressBarProperty; // CheckBox typedef enum { - CHECK_TEXT_PADDING = 16 + CHECK_PADDING = 16 } GuiCheckBoxProperty; // ComboBox typedef enum { - SELECTOR_WIDTH = 16, - SELECTOR_PADDING + COMBO_BUTTON_WIDTH = 16, + COMBO_BUTTON_PADDING } GuiComboBoxProperty; // DropdownBox typedef enum { - ARROW_RIGHT_PADDING = 16, + ARROW_PADDING = 16, + DROPDOWN_ITEMS_PADDING } GuiDropdownBoxProperty; // TextBox / TextBoxMulti / ValueBox / Spinner typedef enum { - MULTILINE_PADDING = 16, + TEXT_INNER_PADDING = 16, + TEXT_LINES_PADDING, COLOR_SELECTED_FG, COLOR_SELECTED_BG } GuiTextBoxProperty; +// Spinner typedef enum { - SELECT_BUTTON_WIDTH = 16, - SELECT_BUTTON_PADDING, - SELECT_BUTTON_BORDER_WIDTH + SPIN_BUTTON_WIDTH = 16, + SPIN_BUTTON_PADDING, } GuiSpinnerProperty; // ScrollBar typedef enum { ARROWS_SIZE = 16, - SLIDER_PADDING, - SLIDER_SIZE, + ARROWS_VISIBLE, + SCROLL_SLIDER_PADDING, + SCROLL_SLIDER_SIZE, + SCROLL_PADDING, SCROLL_SPEED, - ARROWS_VISIBLE } GuiScrollBarProperty; // ScrollBar side @@ -350,19 +390,19 @@ typedef enum { // ListView typedef enum { - ELEMENTS_HEIGHT = 16, - ELEMENTS_PADDING, + LIST_ITEMS_HEIGHT = 16, + LIST_ITEMS_PADDING, SCROLLBAR_WIDTH, - SCROLLBAR_SIDE, // This property defines vertical scrollbar side (SCROLLBAR_LEFT_SIDE or SCROLLBAR_RIGHT_SIDE) + SCROLLBAR_SIDE, } GuiListViewProperty; // ColorPicker typedef enum { COLOR_SELECTOR_SIZE = 16, - BAR_WIDTH, // Lateral bar width - BAR_PADDING, // Lateral bar separation from panel - BAR_SELECTOR_HEIGHT, // Lateral bar selector height - BAR_SELECTOR_PADDING // Lateral bar selector outer padding + HUEBAR_WIDTH, // Right hue bar width + HUEBAR_PADDING, // Right hue bar separation from panel + HUEBAR_SELECTOR_HEIGHT, // Right hue bar selector height + HUEBAR_SELECTOR_OVERFLOW // Right hue bar selector overflow } GuiColorPickerProperty; //---------------------------------------------------------------------------------- @@ -374,41 +414,32 @@ typedef enum { // Module Functions Declaration //---------------------------------------------------------------------------------- -// Global gui modification functions +// State modification functions RAYGUIDEF void GuiEnable(void); // Enable gui controls (global state) RAYGUIDEF void GuiDisable(void); // Disable gui controls (global state) RAYGUIDEF void GuiLock(void); // Lock gui controls (global state) RAYGUIDEF void GuiUnlock(void); // Unlock gui controls (global state) -RAYGUIDEF void GuiState(int state); // Set gui state (global state) -RAYGUIDEF void GuiFont(Font font); // Set gui custom font (global state) RAYGUIDEF void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +RAYGUIDEF void GuiSetState(int state); // Set gui state (global state) +RAYGUIDEF int GuiGetState(void); // Get gui state (global state) + +// Font set/get functions +RAYGUIDEF void GuiSetFont(Font font); // Set gui custom font (global state) +RAYGUIDEF Font GuiGetFont(void); // Get gui custom font (global state) // Style set/get functions RAYGUIDEF void GuiSetStyle(int control, int property, int value); // Set one style property RAYGUIDEF int GuiGetStyle(int control, int property); // Get one style property -#if defined(RAYGUI_TEXTBOX_EXTENDED) -// GuiTextBox() extended functions -RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds); // Sets the active textbox -RAYGUIDEF Rectangle GuiTextBoxGetActive(void); // Get bounds of active textbox -RAYGUIDEF void GuiTextBoxSetCursor(int cursor); // Set cursor position of active textbox -RAYGUIDEF int GuiTextBoxGetCursor(void); // Get cursor position of active textbox -RAYGUIDEF void GuiTextBoxSetSelection(int start, int length); // Set selection of active textbox -RAYGUIDEF Vector2 GuiTextBoxGetSelection(void); // Get selection of active textbox (x - selection start y - selection length) -RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds); // Returns true if a textbox control with specified `bounds` is the active textbox -RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void); // Get state for the active textbox -RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state); // Set state for the active textbox (state must be valid else things will break) -RAYGUIDEF void GuiTextBoxSelectAll(const char *text); // Select all characters in the active textbox (same as pressing `CTRL` + `A`) -RAYGUIDEF void GuiTextBoxCopy(const char *text); // Copy selected text to clipboard from the active textbox (same as pressing `CTRL` + `C`) -RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize); // Paste text from clipboard into the textbox (same as pressing `CTRL` + `V`) -RAYGUIDEF void GuiTextBoxCut(char *text); // Cut selected text in the active textbox and copy it to clipboard (same as pressing `CTRL` + `X`) -RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before); // Deletes a character or selection before from the active textbox (depending on `before`). Returns bytes deleted. -RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to); // Get the byte index for a character starting at position `from` with index `start` until position `to`. -#endif +// Tooltips set functions +RAYGUIDEF void GuiEnableTooltip(void); // Enable gui tooltips +RAYGUIDEF void GuiDisableTooltip(void); // Disable gui tooltips +RAYGUIDEF void GuiSetTooltip(const char *tooltip); // Set current tooltip for display +RAYGUIDEF void GuiClearTooltip(void); // Clear any tooltip registered // Container/separator controls, useful for controls organization -RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text); // Window Box control, shows a window that can be closed -RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with title name +RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed +RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name RAYGUIDEF void GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text RAYGUIDEF void GuiPanel(Rectangle bounds); // Panel control, useful to group controls RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll); // Scroll Panel control @@ -417,37 +448,38 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text); // Label control, shows text RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked -RAYGUIDEF bool GuiImageButton(Rectangle bounds, Texture2D texture); // Image button control, returns true when clicked -RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle texSource, const char *text); // Image button extended control, returns true when clicked +RAYGUIDEF bool GuiImageButton(Rectangle bounds, const char *text, Texture2D texture); // Image button control, returns true when clicked +RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, const char *text, Texture2D texture, Rectangle texSource); // Image button extended control, returns true when clicked RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active); // Toggle Button control, returns true when active RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active); // Toggle Group control, returns active toggle index RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked); // Check Box control, returns true when active RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active); // Combo Box control, returns selected item index RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item -RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value -RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers +RAYGUIDEF bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value +RAYGUIDEF bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines -RAYGUIDEF float GuiSlider(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Slider control, returns selected value -RAYGUIDEF float GuiSliderBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Slider Bar control, returns selected value -RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Progress Bar control, shows current progress value +RAYGUIDEF float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider control, returns selected value +RAYGUIDEF float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider Bar control, returns selected value +RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Progress Bar control, shows current progress value RAYGUIDEF void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text RAYGUIDEF void GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll Bar control RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs); // Grid control // Advance controls set -RAYGUIDEF bool GuiListView(Rectangle bounds, const char *text, int *active, int *scrollIndex, bool editMode); // List View control, returns selected list element index -RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int *enabled, int *active, int *focus, int *scrollIndex, bool editMode); // List View with extended parameters -RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const char *message, const char *buttons); // Message Box control, displays a message -RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *windowTitle, const char *message, char *text, const char *buttons); // Text Input Box control, ask for text -RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color); // Color Picker control +RAYGUIDEF int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active); // List View control, returns selected list item index +RAYGUIDEF int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, int *scrollIndex, int active); // List View with extended parameters +RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message +RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text); // Text Input Box control, ask for text +RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color); // Color Picker control (multiple color controls) +RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color); // Color Panel control +RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha); // Color Bar Alpha control +RAYGUIDEF float GuiColorBarHue(Rectangle bounds, float value); // Color Bar Hue control // Styles loading functions RAYGUIDEF void GuiLoadStyle(const char *fileName); // Load style file (.rgs) -RAYGUIDEF void GuiLoadStyleProps(const int *props, int count); // Load style properties from array RAYGUIDEF void GuiLoadStyleDefault(void); // Load style default over global style -RAYGUIDEF void GuiUpdateStyleComplete(void); // Updates full style properties set with default values /* typedef GuiStyle (unsigned int *) @@ -455,7 +487,20 @@ RAYGUIDEF GuiStyle LoadGuiStyle(const char *fileName); // Load style fr RAYGUIDEF void UnloadGuiStyle(GuiStyle style); // Unload style */ -RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended +RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported) + +#if defined(RAYGUI_SUPPORT_ICONS) +// Gui icons functionality +RAYGUIDEF void GuiDrawIcon(int iconId, Vector2 position, int pixelSize, Color color); + +RAYGUIDEF unsigned int *GuiGetIcons(void); // Get full icons data pointer +RAYGUIDEF unsigned int *GuiGetIconData(int iconId); // Get icon bit data +RAYGUIDEF void GuiSetIconData(int iconId, unsigned int *data); // Set icon bit data + +RAYGUIDEF void GuiSetIconPixel(int iconId, int x, int y); // Set icon pixel value +RAYGUIDEF void GuiClearIconPixel(int iconId, int x, int y); // Clear icon pixel value +RAYGUIDEF bool GuiCheckIconPixel(int iconId, int x, int y); // Check icon pixel value +#endif #endif // RAYGUI_H @@ -468,20 +513,17 @@ RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text wit #if defined(RAYGUI_IMPLEMENTATION) -#if defined(RAYGUI_RICONS_SUPPORT) - #if defined(RAYGUI_STANDALONE) - #define RICONS_STANDALONE - #endif - +#if defined(RAYGUI_SUPPORT_ICONS) #define RICONS_IMPLEMENTATION - #include "ricons.h" // Required for: raygui icons + #include "ricons.h" // Required for: raygui icons data #endif -#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() -#include // Required for: strlen() on GuiTextBox() +#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() +#include // Required for: strlen() on GuiTextBox() +#include // Required for: roundf() on GuiColorPicker() #if defined(RAYGUI_STANDALONE) - #include // Required for: va_list, va_start(), vfprintf(), va_end() + #include // Required for: va_list, va_start(), vfprintf(), va_end() #endif #ifdef __cplusplus @@ -498,7 +540,7 @@ RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text wit //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -// Gui control property style element +// Gui control property style color element typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; //---------------------------------------------------------------------------------- @@ -506,21 +548,20 @@ typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; //---------------------------------------------------------------------------------- static GuiControlState guiState = GUI_STATE_NORMAL; -static Font guiFont = { 0 }; // NOTE: Highly coupled to raylib -static bool guiLocked = false; -static float guiAlpha = 1.0f; +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 element transpacency on drawing -// Global gui style array (allocated on heap by default) +// Global gui style array (allocated on data segment by default) // NOTE: In raygui we manage a single int array with all the possible style properties. // When a new style is loaded, it loads over the global style... but default gui style // could always be recovered with GuiLoadStyleDefault() static unsigned int guiStyle[NUM_CONTROLS*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED)] = { 0 }; -static bool guiStyleLoaded = false; +static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization -#if defined(RAYGUI_TEXTBOX_EXTENDED) -static Rectangle guiTextBoxActive = { 0 }; // Area of the currently active textbox -static GuiTextBoxState guiTextBoxState = { .cursor = -1, .start = 0, .index = 0, .select = -1 }; // Keeps state of the active textbox -#endif +// Tooltips required variables +static const char *guiTooltip = NULL; // Gui tooltip currently active (user provided) +static bool guiTooltipEnabled = true; // Gui tooltips enabled //---------------------------------------------------------------------------------- // Standalone Mode Functions Declaration @@ -536,6 +577,7 @@ static GuiTextBoxState guiTextBoxState = { .cursor = -1, .start = 0, .index = 0, #define KEY_UP 265 #define KEY_BACKSPACE 259 #define KEY_ENTER 257 + #define MOUSE_LEFT_BUTTON 0 // Input required functions @@ -548,24 +590,29 @@ static bool IsMouseButtonReleased(int button); static bool IsKeyDown(int key); static bool IsKeyPressed(int key); -static int GetKeyPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox() +static int GetCharPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox() //------------------------------------------------------------------------------- // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color); +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle(), GuiDrawIcon() + static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() static void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // -- GuiDropdownBox(), GuiScrollBar() static void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // -- GuiImageButtonEx() + +static void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // -- GuiTextBoxMulti() //------------------------------------------------------------------------------- // Text required functions //------------------------------------------------------------------------------- -static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() static Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // -- GetTextWidth(), GuiTextBoxMulti() static void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // -- GuiDrawText() static Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount); // -- GuiLoadStyle() +static char *LoadText(const char *fileName); // -- GuiLoadStyle() +static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle() //------------------------------------------------------------------------------- // raylib functions already implemented in raygui @@ -575,9 +622,9 @@ static int ColorToInt(Color color); // Returns hexadecimal value static Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f 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 void DrawRectangleRec(Rectangle rec, Color color); // Draw rectangle filled with color -static void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outlines static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient //------------------------------------------------------------------------------- @@ -586,178 +633,35 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- +static int GetTextWidth(const char *text); // Gui get text width using default font +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 -// List Element control, returns element state -static bool GuiListElement(Rectangle bounds, const char *text, bool active, bool editMode); +static void GuiDrawText(const char *text, Rectangle bounds, 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 void GuiDrawTooltip(Rectangle bounds); // Draw tooltip relatively to bounds -static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB -static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV - -// Gui get text width using default font -static int GetTextWidth(const char *text) // TODO: GetTextSize() -{ - Vector2 size = { 0 }; - - if ((text != NULL) && (text[0] != '\0')) size = MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)); - - // TODO: Consider text icon width here??? - - return (int)size.x; -} - -// Get text bounds considering control bounds -static Rectangle GetTextBounds(int control, Rectangle bounds) -{ - Rectangle textBounds = { 0 }; - - textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING); - textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING); - textBounds.width = bounds.width - 2*(GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING)); - textBounds.height = bounds.height - 2*(GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING)); - - switch (control) - { - case COMBOBOX: bounds.width -= (GuiGetStyle(control, SELECTOR_WIDTH) + GuiGetStyle(control, SELECTOR_PADDING)); break; - case CHECKBOX: bounds.x += (bounds.width + GuiGetStyle(control, CHECK_TEXT_PADDING)); break; - default: break; - } - - // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, SPINNER, LISTVIEW (scrollbar?) - // More special cases (label side): CHECKBOX, SLIDER - - return textBounds; -} - -// Get text icon if provided and move text cursor -static const char *GetTextIcon(const char *text, int *iconId) -{ -#if defined(RAYGUI_RICONS_SUPPORT) - if (text[0] == '#') // Maybe we have an icon! - { - char iconValue[4] = { 0 }; - - int i = 1; - for (i = 1; i < 4; i++) - { - if ((text[i] != '#') && (text[i] != '\0')) iconValue[i - 1] = text[i]; - else break; - } - - iconValue[3] = '\0'; - *iconId = atoi(iconValue); - - // Move text pointer after icon - // WARNING: If only icon provided, it could point to EOL character! - if (*iconId > 0) text += (i + 1); - } -#endif - - return text; -} - -// Gui draw text using default font -static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) -{ - #define VALIGN_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect - - if ((text != NULL) && (text[0] != '\0')) - { - int iconId = 0; - text = GetTextIcon(text, &iconId); // Check text for icon and move cursor - - // Get text position depending on alignment and iconId - //--------------------------------------------------------------------------------- - #define ICON_TEXT_PADDING 4 - - Vector2 position = { bounds.x, bounds.y }; - - // NOTE: We get text size after icon been processed - int textWidth = GetTextWidth(text); - int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); - -#if defined(RAYGUI_RICONS_SUPPORT) - if (iconId > 0) - { - textWidth += RICONS_SIZE; - - // WARNING: If only icon provided, text could be pointing to eof character! - if ((text != NULL) && (text[0] != '\0')) textWidth += ICON_TEXT_PADDING; - } -#endif - // Check guiTextAlign global variables - switch (alignment) - { - case GUI_TEXT_ALIGN_LEFT: - { - position.x = bounds.x; - position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height); - } break; - case GUI_TEXT_ALIGN_CENTER: - { - position.x = bounds.x + bounds.width/2 - textWidth/2; - position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height); - } break; - case GUI_TEXT_ALIGN_RIGHT: - { - position.x = bounds.x + bounds.width - textWidth; - position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height); - } break; - default: break; - } - //--------------------------------------------------------------------------------- - - // Draw text (with icon if available) - //--------------------------------------------------------------------------------- -#if defined(RAYGUI_RICONS_SUPPORT) - #define ICON_TEXT_PADDING 4 - - if (iconId > 0) - { - // NOTE: We consider icon height, probably different than text size - DrawIcon(iconId, RAYGUI_CLITERAL(Vector2){ position.x, bounds.y + bounds.height/2 - RICONS_SIZE/2 + VALIGN_OFFSET(bounds.height) }, 1, tint); - position.x += (RICONS_SIZE + ICON_TEXT_PADDING); - } -#endif - DrawTextEx(guiFont, text, position, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), tint); - //--------------------------------------------------------------------------------- - } -} - -// Split controls text into multiple strings -// Also check for multiple columns (required by GuiToggleGroup()) -static const char **GuiTextSplit(const char *text, int *count, int *textRow); +static const char **GuiTextSplit(const char *text, 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 //---------------------------------------------------------------------------------- -// Module Functions Definition +// Gui Setup Functions Definition //---------------------------------------------------------------------------------- - // Enable gui global state -RAYGUIDEF void GuiEnable(void) { guiState = GUI_STATE_NORMAL; } +void GuiEnable(void) { guiState = GUI_STATE_NORMAL; } // Disable gui global state -RAYGUIDEF void GuiDisable(void) { guiState = GUI_STATE_DISABLED; } +void GuiDisable(void) { guiState = GUI_STATE_DISABLED; } // Lock gui global state -RAYGUIDEF void GuiLock(void) { guiLocked = true; } +void GuiLock(void) { guiLocked = true; } // Unlock gui global state -RAYGUIDEF void GuiUnlock(void) { guiLocked = false; } - -// Set gui state (global state) -RAYGUIDEF void GuiState(int state) { guiState = (GuiControlState)state; } - -// Define custom gui font -RAYGUIDEF void GuiFont(Font font) -{ - if (font.texture.id > 0) - { - guiFont = font; - GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); - } -} +void GuiUnlock(void) { guiLocked = false; } // Set gui controls alpha global state -RAYGUIDEF void GuiFade(float alpha) +void GuiFade(float alpha) { if (alpha < 0.0f) alpha = 0.0f; else if (alpha > 1.0f) alpha = 1.0f; @@ -765,89 +669,90 @@ RAYGUIDEF void GuiFade(float alpha) guiAlpha = alpha; } +// Set gui state (global state) +void GuiSetState(int state) { guiState = (GuiControlState)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; + GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); + } +} + +// Get custom gui font +Font GuiGetFont(void) +{ + return guiFont; +} + // Set control style property value -RAYGUIDEF void GuiSetStyle(int control, int property, int value) +void GuiSetStyle(int control, int property, int value) { if (!guiStyleLoaded) GuiLoadStyleDefault(); guiStyle[control*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property] = value; + + // Default properties are propagated to all controls + if ((control == 0) && (property < NUM_PROPS_DEFAULT)) + { + for (int i = 1; i < NUM_CONTROLS; i++) guiStyle[i*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property] = value; + } } // Get control style property value -RAYGUIDEF int GuiGetStyle(int control, int property) +int GuiGetStyle(int control, int property) { if (!guiStyleLoaded) GuiLoadStyleDefault(); return guiStyle[control*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property]; } -#if defined(RAYGUI_TEXTBOX_EXTENDED) -// Sets the active textbox (reseting state of the previous active textbox) -RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds) -{ - guiTextBoxActive = bounds; - guiTextBoxState = (GuiTextBoxState){ .cursor = -1, .start = 0, .index = 0, .select = -1 }; -} +// Enable gui tooltips +void GuiEnableTooltip(void) { guiTooltipEnabled = true; } -// Gets bounds of active textbox -RAYGUIDEF Rectangle GuiTextBoxGetActive(void) { return guiTextBoxActive; } +// Disable gui tooltips +void GuiDisableTooltip(void) { guiTooltipEnabled = false; } -// Set cursor position of active textbox -RAYGUIDEF void GuiTextBoxSetCursor(int cursor) -{ - guiTextBoxState.cursor = (cursor < 0) ? -1 : cursor; - guiTextBoxState.start = -1; // Mark this to be recalculated -} +// Set current tooltip for display +void GuiSetTooltip(const char *tooltip) { guiTooltip = tooltip; } -// Get cursor position of active textbox -RAYGUIDEF int GuiTextBoxGetCursor(void) { return guiTextBoxState.cursor; } +// Clear any tooltip registered +void GuiClearTooltip(void) { guiTooltip = NULL; } -// Set selection of active textbox -RAYGUIDEF void GuiTextBoxSetSelection(int start, int length) -{ - if(start < 0) start = 0; - if(length < 0) length = 0; - GuiTextBoxSetCursor(start + length); - guiTextBoxState.select = start; -} -// Get selection of active textbox -RAYGUIDEF Vector2 GuiTextBoxGetSelection(void) -{ - if(guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor) - return RAYGUI_CLITERAL(Vector2){ 0 }; - else if(guiTextBoxState.cursor > guiTextBoxState.select) - return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.select, guiTextBoxState.cursor - guiTextBoxState.select }; - - return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.cursor, guiTextBoxState.select - guiTextBoxState.cursor }; -} - -// Returns true if a textbox control with specified `bounds` is the active textbox -RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds) -{ - return (bounds.x == guiTextBoxActive.x && bounds.y == guiTextBoxActive.y && - bounds.width == guiTextBoxActive.width && bounds.height == guiTextBoxActive.height); -} -RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void) { return guiTextBoxState; } -RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state) -{ - // NOTE: should we check if state values are valid ?!? - guiTextBoxState = state; -} -#endif +//---------------------------------------------------------------------------------- +// Gui Controls Functions Definition +//---------------------------------------------------------------------------------- // Window Box control -RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) +bool GuiWindowBox(Rectangle bounds, const char *title) { - #define WINDOW_CLOSE_BUTTON_PADDING 2 - #define WINDOW_STATUSBAR_HEIGHT 24 + // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox() + #define WINDOW_STATUSBAR_HEIGHT 22 - GuiControlState state = guiState; + //GuiControlState state = guiState; bool clicked = false; - Rectangle statusBar = { bounds.x, bounds.y, bounds.width, WINDOW_STATUSBAR_HEIGHT }; - if (bounds.height < WINDOW_STATUSBAR_HEIGHT*2) bounds.height = WINDOW_STATUSBAR_HEIGHT*2; + int statusBarHeight = WINDOW_STATUSBAR_HEIGHT + 2*GuiGetStyle(STATUSBAR, BORDER_WIDTH); + statusBarHeight += (statusBarHeight%2); + + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, statusBarHeight }; + if (bounds.height < statusBarHeight*2) bounds.height = statusBarHeight*2; + + Rectangle windowPanel = { bounds.x, bounds.y + statusBarHeight - 1, bounds.width, bounds.height - statusBarHeight }; + Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20, + statusBar.y + statusBarHeight/2 - 18/2, 18, 18 }; - Rectangle buttonRec = { statusBar.x + statusBar.width - GuiGetStyle(DEFAULT, BORDER_WIDTH) - WINDOW_CLOSE_BUTTON_PADDING - 20, - statusBar.y + GuiGetStyle(DEFAULT, BORDER_WIDTH) + WINDOW_CLOSE_BUTTON_PADDING, 18, 18 }; // Update control //-------------------------------------------------------------------- // NOTE: Logic is directly managed by button @@ -855,31 +760,18 @@ RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - - // Draw window base - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, BORDER + (state*3))), guiAlpha)); - DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), - bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 }, - Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); - - // Draw window header as status bar - int defaultPadding = GuiGetStyle(DEFAULT, INNER_PADDING); - int defaultTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); - GuiSetStyle(DEFAULT, INNER_PADDING, 8); - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); - GuiStatusBar(statusBar, text); - GuiSetStyle(DEFAULT, INNER_PADDING, defaultPadding); - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, defaultTextAlign); - + GuiStatusBar(statusBar, title); // Draw window header as status bar + GuiPanel(windowPanel); // 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, GUI_TEXT_ALIGN_CENTER); -#if defined(RAYGUI_RICONS_SUPPORT) - clicked = GuiButton(buttonRec, GuiIconText(RICON_CROSS_SMALL, NULL)); +#if defined(RAYGUI_SUPPORT_ICONS) + clicked = GuiButton(closeButtonRec, GuiIconText(RICON_CROSS_SMALL, NULL)); #else - clicked = GuiButton(buttonRec, "x"); + clicked = GuiButton(closeButtonRec, "x"); #endif GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); @@ -888,31 +780,28 @@ RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) return clicked; } -// Group Box control with title name -RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text) +// Group Box control with text name +void GuiGroupBox(Rectangle bounds, const char *text) { #define GROUPBOX_LINE_THICK 1 #define GROUPBOX_TEXT_PADDING 10 - #define GROUPBOX_PADDING 2 GuiControlState state = guiState; // Draw control //-------------------------------------------------------------------- - DrawRectangle(bounds.x, bounds.y, GROUPBOX_LINE_THICK, bounds.height, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - DrawRectangle(bounds.x, bounds.y + bounds.height - 1, bounds.width, GROUPBOX_LINE_THICK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - DrawRectangle(bounds.x + bounds.width - 1, bounds.y, GROUPBOX_LINE_THICK, bounds.height, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, GROUPBOX_LINE_THICK }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, 1 }, text); //-------------------------------------------------------------------- } // Line control -RAYGUIDEF void GuiLine(Rectangle bounds, const char *text) +void GuiLine(Rectangle bounds, const char *text) { - #define LINE_THICK 1 #define LINE_TEXT_PADDING 10 - #define LINE_TEXT_SPACING 2 GuiControlState state = guiState; @@ -920,25 +809,25 @@ RAYGUIDEF void GuiLine(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - if (text == NULL) DrawRectangle(bounds.x, bounds.y + bounds.height/2, bounds.width, 1, color); + 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 = GetTextWidth(text) + 2*LINE_TEXT_SPACING; // TODO: Consider text icon + textBounds.width = GetTextWidth(text); // TODO: Consider text icon textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE); - textBounds.x = bounds.x + LINE_TEXT_PADDING + LINE_TEXT_SPACING; + textBounds.x = bounds.x + LINE_TEXT_PADDING; textBounds.y = bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; // Draw line with embedded text label: "--- text --------------" - DrawRectangle(bounds.x, bounds.y, LINE_TEXT_PADDING, 1, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, LINE_TEXT_PADDING - 2, 1 }, 0, BLANK, color); GuiLabel(textBounds, text); - DrawRectangle(bounds.x + textBounds.width + LINE_TEXT_PADDING + 2*LINE_TEXT_SPACING, bounds.y, bounds.width - (textBounds.width + LINE_TEXT_PADDING + 2*LINE_TEXT_SPACING), 1, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + LINE_TEXT_PADDING + textBounds.width + 4, bounds.y, bounds.width - textBounds.width - LINE_TEXT_PADDING - 4, 1 }, 0, BLANK, color); } //-------------------------------------------------------------------- } // Panel control -RAYGUIDEF void GuiPanel(Rectangle bounds) +void GuiPanel(Rectangle bounds) { #define PANEL_BORDER_WIDTH 1 @@ -946,13 +835,13 @@ RAYGUIDEF void GuiPanel(Rectangle bounds) // Draw control //-------------------------------------------------------------------- - DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); - DrawRectangleLinesEx(bounds, PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha)); + GuiDrawRectangle(bounds, PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha), + Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); //-------------------------------------------------------------------- } // Scroll Panel control -RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll) +Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll) { GuiControlState state = guiState; @@ -968,8 +857,8 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 const int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; const int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; - const 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 }; - const 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) }; + const 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 }; + const 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) }; // Calculate view area (area without the scrollbars) Rectangle view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? @@ -1023,16 +912,16 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 // Draw control //-------------------------------------------------------------------- - DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background // Save size of the scrollbar slider - const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); + 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, SLIDER_SIZE, ((bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/content.width)*(bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, ((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/content.width)*(bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)); scrollPos.x = -GuiScrollBar(horizontalScrollBar, -scrollPos.x, horizontalMin, horizontalMax); } @@ -1040,7 +929,7 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 if (hasVerticalScrollBar) { // Change scrollbar slider size to show the diff in size between the content height and the widget height - GuiSetStyle(SCROLLBAR, SLIDER_SIZE, ((bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/content.height)* (bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, ((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/content.height)* (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)); scrollPos.y = -GuiScrollBar(verticalScrollBar, -scrollPos.y, verticalMin, verticalMax); } @@ -1048,17 +937,15 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 if (hasHorizontalScrollBar && hasVerticalScrollBar) { // TODO: Consider scroll bars side - DrawRectangle(horizontalScrollBar.x + horizontalScrollBar.width + 2, - verticalScrollBar.y + verticalScrollBar.height + 2, - horizontalScrollBarWidth - 4, verticalScrollBarWidth - 4, - Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state * 3))), guiAlpha)); + Rectangle corner = { horizontalScrollBar.x + horizontalScrollBar.width + 2, verticalScrollBar.y + verticalScrollBar.height + 2, horizontalScrollBarWidth - 4, verticalScrollBarWidth - 4 }; + GuiDrawRectangle(corner, 0, BLANK, Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))), guiAlpha)); } - // Set scrollbar slider size back to the way it was before - GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider); - // Draw scrollbar lines depending on current state - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, (float)BORDER + (state*3))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, (float)BORDER + (state*3))), guiAlpha), BLANK); + + // Set scrollbar slider size back to the way it was before + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); //-------------------------------------------------------------------- if (scroll != NULL) *scroll = scrollPos; @@ -1067,7 +954,7 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 } // Label control -RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text) +void GuiLabel(Rectangle bounds, const char *text) { GuiControlState state = guiState; @@ -1083,7 +970,7 @@ RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text) } // Button control, returns true when clicked -RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text) +bool GuiButton(Rectangle bounds, const char *text) { GuiControlState state = guiState; bool pressed = false; @@ -1107,9 +994,7 @@ RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha)); - DrawRectangle(bounds.x + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.y + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); - + GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); //------------------------------------------------------------------ @@ -1117,11 +1002,15 @@ RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text) } // Label button control -RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text) +bool GuiLabelButton(Rectangle bounds, const char *text) { GuiControlState state = guiState; bool pressed = false; + // NOTE: We force bounds.width to be all text + int textWidth = MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)).x; + if (bounds.width < textWidth) bounds.width = textWidth; + // Update control //-------------------------------------------------------------------- if ((state != GUI_STATE_DISABLED) && !guiLocked) @@ -1148,13 +1037,13 @@ RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text) } // Image button control, returns true when clicked -RAYGUIDEF bool GuiImageButton(Rectangle bounds, Texture2D texture) +bool GuiImageButton(Rectangle bounds, const char *text, Texture2D texture) { - return GuiImageButtonEx(bounds, texture, RAYGUI_CLITERAL(Rectangle){ 0, 0, (float)texture.width, (float)texture.height }, NULL); + return GuiImageButtonEx(bounds, text, texture, RAYGUI_CLITERAL(Rectangle){ 0, 0, (float)texture.width, (float)texture.height }); } // Image button control, returns true when clicked -RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle texSource, const char *text) +bool GuiImageButtonEx(Rectangle bounds, const char *text, Texture2D texture, Rectangle texSource) { GuiControlState state = guiState; bool clicked = false; @@ -1177,18 +1066,17 @@ RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle t // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha)); - DrawRectangle(bounds.x + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.y + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); if (text != NULL) GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); - if (texture.id > 0) DrawTextureRec(texture, texSource, RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width/2 - (texSource.width + GuiGetStyle(BUTTON, INNER_PADDING)/2)/2, bounds.y + bounds.height/2 - texSource.height/2 }, Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); + if (texture.id > 0) DrawTextureRec(texture, texSource, RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width/2 - texSource.width/2, bounds.y + bounds.height/2 - texSource.height/2 }, Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); //------------------------------------------------------------------ return clicked; } // Toggle Button control, returns true when active -RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active) +bool GuiToggle(Rectangle bounds, const char *text, bool active) { GuiControlState state = guiState; @@ -1216,16 +1104,12 @@ RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active) //-------------------------------------------------------------------- if (state == GUI_STATE_NORMAL) { - DrawRectangleLinesEx(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha)); - DrawRectangle(bounds.x + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.y + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); - + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, (active? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); } else { - DrawRectangleLinesEx(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha)); - DrawRectangle(bounds.x + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.y + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha)); - + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha)); GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha)); } //-------------------------------------------------------------------- @@ -1234,18 +1118,22 @@ RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active) } // Toggle Group control, returns toggled button index -RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active) +int GuiToggleGroup(Rectangle bounds, const char *text, int active) { + #if !defined(TOGGLEGROUP_MAX_ELEMENTS) + #define TOGGLEGROUP_MAX_ELEMENTS 32 + #endif + float initBoundsX = bounds.x; - // Get substrings elements from text (elements pointers) - int rows[64] = { 0 }; - int elementsCount = 0; - const char **elementsPtrs = GuiTextSplit(text, &elementsCount, rows); + // Get substrings items from text (items pointers) + int rows[TOGGLEGROUP_MAX_ELEMENTS] = { 0 }; + int itemsCount = 0; + const char **items = GuiTextSplit(text, &itemsCount, rows); int prevRow = rows[0]; - for (int i = 0; i < elementsCount; i++) + for (int i = 0; i < itemsCount; i++) { if (prevRow != rows[i]) { @@ -1254,8 +1142,8 @@ RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active) prevRow = rows[i]; } - if (i == active) GuiToggle(bounds, elementsPtrs[i], true); - else if (GuiToggle(bounds, elementsPtrs[i], false) == true) active = i; + if (i == active) GuiToggle(bounds, items[i], true); + else if (GuiToggle(bounds, items[i], false) == true) active = i; bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING)); } @@ -1264,15 +1152,20 @@ RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active) } // Check Box control, returns true when active -RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) +bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) { GuiControlState state = guiState; Rectangle textBounds = { 0 }; - textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING); - textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - textBounds.width = GetTextWidth(text); // TODO: Consider text icon - textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE); + + if (text != NULL) + { + textBounds.width = GetTextWidth(text); + textBounds.height = 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) == GUI_TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING); + } // Update control //-------------------------------------------------------------------- @@ -1280,8 +1173,15 @@ RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) { Vector2 mousePoint = GetMousePosition(); + Rectangle totalBounds = { + (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == GUI_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, RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING), bounds.height })) + if (CheckCollisionPointRec(mousePoint, totalBounds)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; else state = GUI_STATE_FOCUSED; @@ -1293,40 +1193,43 @@ RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha)); - if (checked) DrawRectangle(bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING), - bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING), - bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING)), - bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING)), - Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha), 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, Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)), guiAlpha)); + } - // NOTE: Forced left text alignment - GuiDrawText(text, textBounds, GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + if (text != NULL) GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT)? GUI_TEXT_ALIGN_LEFT : GUI_TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- return checked; } // Combo Box control, returns selected item index -RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) +int GuiComboBox(Rectangle bounds, const char *text, int active) { GuiControlState state = guiState; - bounds.width -= (GuiGetStyle(COMBOBOX, SELECTOR_WIDTH) + GuiGetStyle(COMBOBOX, SELECTOR_PADDING)); + bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_PADDING)); - Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, SELECTOR_PADDING), - (float)bounds.y, (float)GuiGetStyle(COMBOBOX, SELECTOR_WIDTH), (float)bounds.height }; + Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_PADDING), + (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height }; - // Get substrings elements from text (elements pointers, lengths and count) - int elementsCount = 0; - const char **elementsPtrs = GuiTextSplit(text, &elementsCount, NULL); + // Get substrings items from text (items pointers, lengths and count) + int itemsCount = 0; + const char **items = GuiTextSplit(text, &itemsCount, NULL); if (active < 0) active = 0; - else if (active > elementsCount - 1) active = elementsCount - 1; + else if (active > itemsCount - 1) active = itemsCount - 1; // Update control //-------------------------------------------------------------------- - if ((state != GUI_STATE_DISABLED) && !guiLocked) + if ((state != GUI_STATE_DISABLED) && !guiLocked && (itemsCount > 1)) { Vector2 mousePoint = GetMousePosition(); @@ -1336,7 +1239,7 @@ RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { active += 1; - if (active >= elementsCount) active = 0; + if (active >= itemsCount) active = 0; } if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; @@ -1348,10 +1251,8 @@ RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) // Draw control //-------------------------------------------------------------------- // Draw combo box main - DrawRectangleLinesEx(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha)); - DrawRectangle(bounds.x + GuiGetStyle(COMBOBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(COMBOBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(COMBOBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha)); - - GuiDrawText(elementsPtrs[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha)); + GuiDrawText(items[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); // Draw selector using a custom button // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values @@ -1360,7 +1261,7 @@ RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); - GuiButton(selector, TextFormat("%i/%i", active + 1, elementsCount)); + GuiButton(selector, TextFormat("%i/%i", active + 1, itemsCount)); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); @@ -1369,1155 +1270,269 @@ RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) return active; } -// Dropdown Box control, returns selected item -RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) +// Dropdown Box control +// NOTE: Returns mouse click +bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) { GuiControlState state = guiState; + int itemSelected = *active; + int itemFocused = -1; - // Get substrings elements from text (elements pointers, lengths and count) - int elementsCount = 0; - const char **elementsPtrs = GuiTextSplit(text, &elementsCount, NULL); + // Get substrings items from text (items pointers, lengths and count) + int itemsCount = 0; + const char **items = GuiTextSplit(text, &itemsCount, NULL); - bool pressed = false; - int auxActive = *active; + Rectangle boundsOpen = bounds; + boundsOpen.height = (itemsCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING)); - Rectangle closeBounds = bounds; - Rectangle openBounds = bounds; + Rectangle itemBounds = bounds; - openBounds.height *= (elementsCount + 1); + bool pressed = false; // Check mouse button pressed // Update control //-------------------------------------------------------------------- - if (guiLocked && editMode) guiLocked = false; + if ((state != GUI_STATE_DISABLED) && !guiLocked && (itemsCount > 1)) + { + Vector2 mousePoint = GetMousePosition(); + if (editMode) + { + state = GUI_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)) pressed = true; + } + + // Check if already selected item has been pressed again + if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + + // Check focused and selected item + for (int i = 0; i < itemsCount; i++) + { + // Update item rectangle y position for next item + itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING)); + + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = i; + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + itemSelected = i; + pressed = true; // Item selected, change to editMode = false + } + break; + } + } + + itemBounds = bounds; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + pressed = true; + state = GUI_STATE_PRESSED; + } + else state = GUI_STATE_FOCUSED; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (editMode) GuiPanel(boundsOpen); + + GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)), guiAlpha)); + GuiDrawText(items[itemSelected], GetTextBounds(DEFAULT, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)), guiAlpha)); + + if (editMode) + { + // Draw visible items + for (int i = 0; i < itemsCount; i++) + { + // Update item rectangle y position for next item + itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING)); + + if (i == itemSelected) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)), guiAlpha)); + GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)), guiAlpha)); + } + else if (i == itemFocused) + { + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)), guiAlpha)); + GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)), guiAlpha)); + } + else GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)), guiAlpha)); + } + } + + // TODO: Avoid this function, use icon instead or 'v' + DrawTriangle(RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2 }, + RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING) + 5, bounds.y + bounds.height/2 - 2 + 5 }, + RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING) + 10, bounds.y + bounds.height/2 - 2 }, + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + + //GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, + // GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + *active = itemSelected; + return pressed; +} + +// Text Box control, updates input text +// NOTE 1: Requires static variables: framesCounter +// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) +bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +{ + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + Rectangle cursor = { + bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text) + 2, + bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), + 1, + GuiGetStyle(DEFAULT, TEXT_SIZE)*2 + }; + + // Update control + //-------------------------------------------------------------------- if ((state != GUI_STATE_DISABLED) && !guiLocked) { Vector2 mousePoint = GetMousePosition(); - if (editMode) state = GUI_STATE_PRESSED; - - if (!editMode) - { - if (CheckCollisionPointRec(mousePoint, closeBounds)) - { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; - - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; - else state = GUI_STATE_FOCUSED; - } - } - else - { - if (CheckCollisionPointRec(mousePoint, closeBounds)) - { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; - } - else if (!CheckCollisionPointRec(mousePoint, openBounds)) - { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; - } - } - } - //-------------------------------------------------------------------- - - // Draw control - //-------------------------------------------------------------------- - - // TODO: Review this ugly hack... DROPDOWNBOX depends on GuiListElement() that uses DEFAULT_TEXT_ALIGNMENT - int tempTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT)); - - switch (state) - { - case GUI_STATE_NORMAL: - { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_NORMAL)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_NORMAL)), guiAlpha)); - - GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); - } break; - case GUI_STATE_FOCUSED: - { - GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, editMode); - } break; - case GUI_STATE_PRESSED: - { - if (!editMode) GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); - if (editMode) - { - GuiPanel(openBounds); - - GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); - - for (int i = 0; i < elementsCount; i++) - { - if (i == auxActive && editMode) - { - if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height*(i + 1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), - bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, - elementsPtrs[i], true, true) == false) pressed = true; - } - else - { - if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height*(i+1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), - bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, - elementsPtrs[i], false, true)) - { - auxActive = i; - pressed = true; - } - } - } - } - } break; - case GUI_STATE_DISABLED: - { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_DISABLED)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_DISABLED)), guiAlpha)); - - GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); - } break; - default: break; - } - - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, tempTextAlign); - - // TODO: Avoid this function, use icon instead or 'v' - DrawTriangle(RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2 }, - RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 5, bounds.y + bounds.height/2 - 2 + 5 }, - RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 10, bounds.y + bounds.height/2 - 2 }, - Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); - - //GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, - // GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); - //-------------------------------------------------------------------- - - *active = auxActive; - return pressed; -} - -#if defined(RAYGUI_TEXTBOX_EXTENDED) -// Spinner control, returns selected value -// NOTE: Requires static variables: timer, valueSpeed - ERROR! -RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) -{ - #define GUI_SPINNER_HOLD_SPEED 0.2f // Min 200ms delay - - static float timer = 0.0f; - - int tempValue = *value; - const float time = GetTime(); // Get current time - bool pressed = false, active = GuiTextBoxIsActive(bounds); - - Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING), bounds.y, - bounds.width - 2*(GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING)), bounds.height }; - Rectangle leftButtonBound = { bounds.x, bounds.y, GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.height }; - Rectangle rightButtonBound = { bounds.x + bounds.width - GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.y, GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.height }; - - // Update control - //-------------------------------------------------------------------- - Vector2 mouse = GetMousePosition(); - if (tempValue < minValue) tempValue = minValue; - if (tempValue > maxValue) tempValue = maxValue; - - if (editMode) - { - if (!active) - { - // This becomes the active textbox when mouse is pressed or held inside bounds - if ((IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonDown(MOUSE_LEFT_BUTTON)) && - CheckCollisionPointRec(mouse, bounds)) - { - GuiTextBoxSetActive(bounds); - active = true; - } - } - } - - // Reset timer when one of the buttons is clicked (without this, holding the button down will not behave correctly) - if ((CheckCollisionPointRec(mouse, leftButtonBound) || CheckCollisionPointRec(mouse, rightButtonBound)) && - IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) - { - timer = time; - } - //-------------------------------------------------------------------- - - // Draw control - //-------------------------------------------------------------------- - if (GuiTextBoxIsActive(bounds)) guiTextBoxActive = spinner; // Set our spinner as the active textbox - pressed = GuiValueBox(spinner, &tempValue, minValue, maxValue, editMode); - if (GuiTextBoxIsActive(spinner)) guiTextBoxActive = bounds; // Revert change - - // Draw value selector custom buttons - // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values - int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); - GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH)); - - int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); - GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); - - char *icon = "<"; -#if defined(RAYGUI_RICONS_SUPPORT) - icon = (char *)GuiIconText(RICON_ARROW_LEFT_FILL, NULL); -#endif - if (GuiButton(leftButtonBound, icon) || // NOTE: also decrease value when the button is held down - (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && - CheckCollisionPointRec(mouse, leftButtonBound) && - (time - timer) > GUI_SPINNER_HOLD_SPEED)) - { - tempValue--; - } - - icon = ">"; -#if defined(RAYGUI_RICONS_SUPPORT) - icon = (char *)GuiIconText(RICON_ARROW_RIGHT_FILL, NULL); -#endif - if (GuiButton(rightButtonBound, icon) || // NOTE: also increase value when the button is held down - (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && - CheckCollisionPointRec(mouse, rightButtonBound) && - (time - timer) > GUI_SPINNER_HOLD_SPEED)) - { - tempValue++; - } - - GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); - GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); - //-------------------------------------------------------------------- - - if (tempValue < minValue) tempValue = minValue; - if (tempValue > maxValue) tempValue = maxValue; - - // Reset timer - if (active && (((time - timer) > GUI_SPINNER_HOLD_SPEED) || (timer == 0.0f) || (timer > time))) timer = time; - - *value = tempValue; - - return pressed; -} - -// Value Box control, updates input text with numbers -RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) -{ - #define VALUEBOX_MAX_CHARS 32 - - char text[VALUEBOX_MAX_CHARS + 1] = { 0 }; - sprintf(text, "%i", *value); - - bool pressed = GuiTextBox(bounds, text, VALUEBOX_MAX_CHARS, editMode); - *value = atoi(text); - - if (*value > maxValue) *value = maxValue; - else if (*value < minValue) *value = minValue; - - return pressed; -} - -enum { - GUI_MEASURE_MODE_CURSOR_END = 0xA, - GUI_MEASURE_MODE_CURSOR_POS, - GUI_MEASURE_MODE_CURSOR_COORDS, -}; - -// Required by GuiTextBox() -// Highly synchronized with calculations in DrawTextRecEx() -static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, int *pos, int mode) -{ - // Get gui font properties - const Font font = guiFont; - const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); - const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); - - int textOffsetX = 0; // Offset between characters - float scaleFactor = 0.0f; - - int letter = 0; // Current character - int index = 0; // Index position in sprite font - - scaleFactor = fontSize/font.baseSize; - - int i = 0, k = 0; - int glyphWidth = 0; - for (i = 0; i < length; i++, k++) - { - glyphWidth = 0; - int next = 1; - letter = GetNextCodepoint(&text[i], &next); - if (letter == 0x3f) next = 1; - index = GetGlyphIndex(font, letter); - i += next - 1; - - if (letter != '\n') - { - glyphWidth = (font.chars[index].advanceX == 0)? - (int)(font.chars[index].rec.width*scaleFactor + spacing): - (int)(font.chars[index].advanceX*scaleFactor + spacing); - - if ((textOffsetX + glyphWidth + 1) >= rec.width) break; - - if ((mode == GUI_MEASURE_MODE_CURSOR_POS) && (*pos == k)) break; - else if (mode == GUI_MEASURE_MODE_CURSOR_COORDS) - { - // Check if the mouse pointer is inside the glyph rect - Rectangle grec = {rec.x + textOffsetX - 1, rec.y, glyphWidth, (font.baseSize + font.baseSize/2)*scaleFactor - 1 }; - Vector2 mouse = GetMousePosition(); - - if (CheckCollisionPointRec(mouse, grec)) - { - // Smooth selection by dividing the glyph rectangle into 2 equal parts and checking where the mouse resides - if (mouse.x > (grec.x + glyphWidth/2)) - { - textOffsetX += glyphWidth; - k++; - } - - break; - } - } - } - else break; - - textOffsetX += glyphWidth; - } - - *pos = k; - - return (rec.x + textOffsetX - 1); -} - -static int GetPrevCodepoint(const char *text, const char *start, int *prev) -{ - int c = 0x3f; - char *p = (char *)text; - *prev = 1; - - for (int i = 0; (p >= start) && (i < 4); p--, i++) - { - if ((((unsigned char)*p) >> 6) != 2) - { - c = GetNextCodepoint(p, prev); - break; - } - } - - return c; -} - -// Required by GuiTextBoxEx() -// Highly synchronized with calculations in DrawTextRecEx() -static int GuiMeasureTextBoxRev(const char *text, int length, Rectangle rec, int *pos) -{ - // Get gui font properties - const Font font = guiFont; - const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); - const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); - - int textOffsetX = 0; // Offset between characters - float scaleFactor = 0.0f; - - int letter = 0; // Current character - int index = 0; // Index position in sprite font - - scaleFactor = fontSize/font.baseSize; - - int i = 0, k = 0; - int glyphWidth = 0, prev = 1; - for (i = length; i >= 0; i--, k++) - { - glyphWidth = 0; - letter = GetPrevCodepoint(&text[i], &text[0], &prev); - - if (letter == 0x3f) prev = 1; - index = GetGlyphIndex(font, letter); - i -= prev - 1; - - if (letter != '\n') - { - glyphWidth = (font.chars[index].advanceX == 0)? - (int)(font.chars[index].rec.width*scaleFactor + spacing): - (int)(font.chars[index].advanceX*scaleFactor + spacing); - - if ((textOffsetX + glyphWidth + 1) >= rec.width) break; - } - else break; - - textOffsetX += glyphWidth; - } - - *pos = k; - - return (i + prev); -} - - -// Calculate cursor coordinates based on the cursor position `pos` inside the `text`. -static inline int GuiTextBoxGetCursorCoordinates(const char *text, int length, Rectangle rec, int pos) -{ - return GuiMeasureTextBox(text, length, rec, &pos, GUI_MEASURE_MODE_CURSOR_POS); -} - -// Calculate cursor position in textbox based on mouse coordinates. -static inline int GuiTextBoxGetCursorFromMouse(const char *text, int length, Rectangle rec, int* pos) -{ - return GuiMeasureTextBox(text, length, rec, pos, GUI_MEASURE_MODE_CURSOR_COORDS); -} - -// Calculates how many characters is the textbox able to draw inside rec -static inline int GuiTextBoxMaxCharacters(const char *text, int length, Rectangle rec) -{ - int pos = -1; - GuiMeasureTextBox(text, length, rec, &pos, GUI_MEASURE_MODE_CURSOR_END); - return pos; -} - -// Returns total number of characters(codepoints) in a UTF8 encoded `text` until `\0` or a `\n` is found. -// NOTE: If a invalid UTF8 sequence is encountered a `?`(0x3f) codepoint is counted instead. -static inline unsigned int GuiCountCodepointsUntilNewline(const char *text) -{ - unsigned int len = 0; - char *ptr = (char*)&text[0]; - - while ((*ptr != '\0') && (*ptr != '\n')) - { - int next = 0; - int letter = GetNextCodepoint(ptr, &next); - - if (letter == 0x3f) ptr += 1; - else ptr += next; - ++len; - } - - return len; -} - -static inline void MoveTextBoxCursorRight(const char* text, int length, Rectangle textRec) -{ - // FIXME: Counting codepoints each time we press the key is expensive, find another way - int count = GuiCountCodepointsUntilNewline(text); - if (guiTextBoxState.cursor < count ) guiTextBoxState.cursor++; - - const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); - - if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) - { - const int cidx = GuiTextBoxGetByteIndex(text, guiTextBoxState.index, guiTextBoxState.start, guiTextBoxState.cursor); - int pos = 0; - guiTextBoxState.index = GuiMeasureTextBoxRev(text, cidx - 1, textRec, &pos); - guiTextBoxState.start = guiTextBoxState.cursor - pos; - } -} - -static inline void MoveTextBoxCursorLeft(const char* text) -{ - if (guiTextBoxState.cursor > 0) guiTextBoxState.cursor--; - - if (guiTextBoxState.cursor < guiTextBoxState.start) - { - int prev = 0; - int letter = GetPrevCodepoint(&text[guiTextBoxState.index - 1], text, &prev); - if (letter == 0x3f) prev = 1; - guiTextBoxState.start--; - guiTextBoxState.index -= prev; - } -} - -RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to) -{ - int i = start, k = from; - - while ((text[i] != '\0') && (k < to)) - { - int j = 0; - int letter = GetNextCodepoint(&text[i], &j); - - if (letter == 0x3f) j = 1; - i += j; - ++k; - } - - return i; -} - -RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before) -{ - if ((guiTextBoxState.cursor != -1) && (text != NULL)) - { - int startIdx = 0, endIdx = 0; - if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) - { - // Delete selection - int start = guiTextBoxState.cursor; - int end = guiTextBoxState.select; - - if (guiTextBoxState.cursor > guiTextBoxState.select) - { - start = guiTextBoxState.select; - end = guiTextBoxState.cursor; - } - - // Convert to byte indexes - startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); - endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); - - // Adjust text box state - guiTextBoxState.cursor = start; // Always set cursor to start of selection - if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame - } - else - { - if (before) - { - // Delete character before cursor - if (guiTextBoxState.cursor != 0) - { - endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - guiTextBoxState.cursor--; - startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - - if (guiTextBoxState.cursor < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame - } - } - else - { - // Delete character after cursor - if (guiTextBoxState.cursor + 1 <= GuiCountCodepointsUntilNewline(text)) - { - startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor+1); - } - } - } - - memmove(&text[startIdx], &text[endIdx], length - endIdx); - text[length - (endIdx - startIdx)] = '\0'; - guiTextBoxState.select = -1; // Always deselect - - return (endIdx - startIdx); - } - - return 0; -} - -RAYGUIDEF void GuiTextBoxSelectAll(const char *text) -{ - guiTextBoxState.cursor = GuiCountCodepointsUntilNewline(text); - - if (guiTextBoxState.cursor > 0) - { - guiTextBoxState.select = 0; - guiTextBoxState.start = -1; // Force recalculate on the next frame - } - else guiTextBoxState.select = -1; -} - -RAYGUIDEF void GuiTextBoxCopy(const char *text) -{ - if ((text != NULL) && - (guiTextBoxState.select != -1) && - (guiTextBoxState.cursor != -1) && - (guiTextBoxState.select != guiTextBoxState.cursor)) - { - int start = guiTextBoxState.cursor; - int end = guiTextBoxState.select; - - if (guiTextBoxState.cursor > guiTextBoxState.select) - { - start = guiTextBoxState.select; - end = guiTextBoxState.cursor; - } - - // Convert to byte indexes - start = GuiTextBoxGetByteIndex(text, 0, 0, start); - end = GuiTextBoxGetByteIndex(text, 0, 0, end); - - // FIXME: `TextSubtext()` only lets use copy MAX_TEXT_BUFFER_LENGTH (1024) bytes - // maybe modify `SetClipboardText()` so we can use it only on part of a string - const char *clipText = TextSubtext(text, start, end - start); - - SetClipboardText(clipText); - } -} - -// Paste text from clipboard into the active textbox. -// `text` is the pointer to the buffer used by the textbox while `textSize` is the text buffer max size -RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize) -{ - const char *clipText = GetClipboardText(); // GLFW guaratees this should be UTF8 encoded! - int length = strlen(text); - - if ((text != NULL) && (clipText != NULL) && (guiTextBoxState.cursor != -1)) - { - if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) - { - // If there's a selection we'll have to delete it first - length -= GuiTextBoxDelete(text, length, true); - } - - int clipLen = strlen(clipText); // We want the length in bytes - - // Calculate how many bytes can we copy from clipboard text before we run out of space - int size = ((length + clipLen) <= textSize) ? clipLen : textSize - length; - - // Make room by shifting to right the bytes after cursor - int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - int endIdx = startIdx + size; - memmove(&text[endIdx], &text[startIdx], length - startIdx); - text[length + size] = '\0'; // Set the NULL char - - // At long last copy the clipboard text - memcpy(&text[startIdx], clipText, size); - - // Set cursor position at the end of the pasted text - guiTextBoxState.cursor = 0; - - for (int i = 0; i < (startIdx + size); guiTextBoxState.cursor++) - { - int next = 0; - int letter = GetNextCodepoint(&text[i], &next); - if (letter != 0x3f) i += next; - else i += 1; - } - - guiTextBoxState.start = -1; // Force to recalculate on the next frame - } -} - -RAYGUIDEF void GuiTextBoxCut(char* text) -{ - if ((text != NULL) && - (guiTextBoxState.select != -1) && - (guiTextBoxState.cursor != -1) && - (guiTextBoxState.select != guiTextBoxState.cursor)) - { - // First copy selection to clipboard; - int start = guiTextBoxState.cursor, end = guiTextBoxState.select; - - if (guiTextBoxState.cursor > guiTextBoxState.select) - { - start = guiTextBoxState.select; - end = guiTextBoxState.cursor; - } - - // Convert to byte indexes - int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); - int endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); - - // FIXME: `TextSubtext()` only lets use copy MAX_TEXT_BUFFER_LENGTH (1024) bytes - // maybe modify `SetClipboardText()` so we can use it only on parts of a string - const char *clipText = TextSubtext(text, startIdx, endIdx - startIdx); - SetClipboardText(clipText); - - // Now delete selection (copy data over it) - int len = strlen(text); - memmove(&text[startIdx], &text[endIdx], len - endIdx); - text[len - (endIdx - startIdx)] = '\0'; - - // Adjust text box state - guiTextBoxState.cursor = start; // Always set cursor to start of selection - if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate - guiTextBoxState.select = -1; // Deselect - } -} - -static int EncodeCodepoint(unsigned int c, char out[5]) -{ - int len = 0; - if (c <= 0x7f) - { - out[0] = (char)c; - len = 1; - } - else if (c <= 0x7ff) - { - out[0] = (char)(((c >> 6) & 0x1f) | 0xc0); - out[1] = (char)((c & 0x3f) | 0x80); - len = 2; - } - else if (c <= 0xffff) - { - out[0] = (char)(((c >> 12) & 0x0f) | 0xe0); - out[1] = (char)(((c >> 6) & 0x3f) | 0x80); - out[2] = (char)((c & 0x3f) | 0x80); - len = 3; - } - else if (c <= 0x10ffff) - { - out[0] = (char)(((c >> 18) & 0x07) | 0xf0); - out[1] = (char)(((c >> 12) & 0x3f) | 0x80); - out[2] = (char)(((c >> 6) & 0x3f) | 0x80); - out[3] = (char)((c & 0x3f) | 0x80); - len = 4; - } - - out[len] = 0; - return len; -} - -// A text box control supporting text selection, cursor positioning and commonly used keyboard shortcuts. -// NOTE 1: Requires static variables: framesCounter -// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) -RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) -{ - // Define the cursor movement/selection speed when movement keys are held/pressed - #define GUI_TEXTBOX_CURSOR_SPEED_MODIFIER 5 - - static int framesCounter = 0; // Required for blinking cursor - - GuiControlState state = guiState; - bool pressed = false; - - // Make sure length doesn't exceed `textSize`. `textSize` is actually the max amount of characters the textbox can handle. - int length = strlen(text); - if (length > textSize) - { - text[textSize] = '\0'; - length = textSize; - } - - // Make sure we have enough room to draw at least 1 character - if ((bounds.width - 2*GuiGetStyle(TEXTBOX, INNER_PADDING)) < GuiGetStyle(DEFAULT, TEXT_SIZE)) - { - bounds.width = GuiGetStyle(DEFAULT, TEXT_SIZE) + 2*GuiGetStyle(TEXTBOX, INNER_PADDING); - } - - // Center the text vertically - int verticalPadding = (bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) - GuiGetStyle(DEFAULT, TEXT_SIZE))/2; - - if (verticalPadding < 0) - { - // Make sure the height is sufficient - bounds.height = 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(DEFAULT, TEXT_SIZE); - verticalPadding = 0; - } - - // Calculate the drawing area for the text inside the control `bounds` - Rectangle textRec = { bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING), - bounds.y + verticalPadding + GuiGetStyle(TEXTBOX, BORDER_WIDTH), - bounds.width - 2*(GuiGetStyle(TEXTBOX, INNER_PADDING) + GuiGetStyle(TEXTBOX, BORDER_WIDTH)), - GuiGetStyle(DEFAULT, TEXT_SIZE) }; - - Vector2 cursorPos = { textRec.x, textRec.y }; // This holds the coordinates inside textRec of the cursor at current position and will be recalculated later - bool active = GuiTextBoxIsActive(bounds); // Check if this textbox is the global active textbox - - int selStart = 0, selLength = 0, textStartIndex = 0; - - // Update control - //-------------------------------------------------------------------- - if ((state != GUI_STATE_DISABLED) && !guiLocked) - { - const Vector2 mousePoint = GetMousePosition(); - if (editMode) { - // Check if we are the global active textbox - // A textbox becomes active when the user clicks it :) - if (!active) - { - if (CheckCollisionPointRec(mousePoint, bounds) && - (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonPressed(MOUSE_RIGHT_BUTTON))) - { - // Hurray!!! we just became the active textbox - active = true; - GuiTextBoxSetActive(bounds); - } - } - else if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) - { - // When active and the right mouse is clicked outside the textbox we should deactivate it - GuiTextBoxSetActive(RAYGUI_CLITERAL(Rectangle){0,0,-1,-1}); // Set a dummy rect as the active textbox bounds - active = false; - } - - if (active) - { - state = GUI_STATE_PRESSED; - framesCounter++; - - // Make sure state doesn't have invalid values - if (guiTextBoxState.cursor > length) guiTextBoxState.cursor = -1; - if (guiTextBoxState.select > length) guiTextBoxState.select = -1; - if (guiTextBoxState.start > length) guiTextBoxState.start = -1; - - - // Check textbox state for changes and recalculate if necesary - if (guiTextBoxState.cursor == -1) - { - // Set cursor to last visible character in textbox - guiTextBoxState.cursor = GuiTextBoxMaxCharacters(text, length, textRec); - } - - if (guiTextBoxState.start == -1) - { - // Force recalculate text start position and text start index - - // NOTE: start and index are always in sync - // start will hold the starting character position from where the text will be drawn - // while index will hold the byte index inside the text for that character + state = GUI_STATE_PRESSED; + framesCounter++; - if (guiTextBoxState.cursor == 0) - { - guiTextBoxState.start = guiTextBoxState.index = 0; // No need to recalculate - } - else - { - int pos = 0; - int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - guiTextBoxState.index = GuiMeasureTextBoxRev(text, len, textRec, &pos); - guiTextBoxState.start = guiTextBoxState.cursor - pos + 1; - } - } - - // ----------------- - // HANDLE KEY INPUT - // ----------------- - // * -> | LSHIFT + -> move cursor to the right | increase selection by one - // * <- | LSHIFT + <- move cursor to the left | decrease selection by one - // * HOME | LSHIFT + HOME moves cursor to start of text | selects text from cursor to start of text - // * END | LSHIFT + END move cursor to end of text | selects text from cursor until end of text - // * CTRL + A select all characters in text - // * CTRL + C copy selected text - // * CTRL + X cut selected text - // * CTRL + V remove selected text, if any, then paste clipboard data - // * DEL delete character or selection after cursor - // * BACKSPACE delete character or selection before cursor - // TODO: Add more shortcuts (insert mode, select word, moveto/select prev/next word ...) - if (IsKeyPressed(KEY_RIGHT) || - (IsKeyDown(KEY_RIGHT) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER == 0))) - { - if (IsKeyDown(KEY_LEFT_SHIFT)) - { - // Selecting - if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start - - MoveTextBoxCursorRight(text, length, textRec); - } - else - { - if (guiTextBoxState.select != -1 && guiTextBoxState.select != guiTextBoxState.cursor) - { - // Deselect and move cursor to end of selection - if (guiTextBoxState.cursor < guiTextBoxState.select) - { - guiTextBoxState.cursor = guiTextBoxState.select - 1; - MoveTextBoxCursorRight(text, length, textRec); - } - } - else - { - // Move cursor to the right - MoveTextBoxCursorRight(text, length, textRec); - } - - guiTextBoxState.select = -1; - } - - framesCounter = 0; - } - else if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER == 0))) - { - if (IsKeyDown(KEY_LEFT_SHIFT)) - { - // Selecting - if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start - - MoveTextBoxCursorLeft(text); - } - else - { - if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) - { - // Deselect and move cursor to start of selection - if (guiTextBoxState.cursor > guiTextBoxState.select) - { - guiTextBoxState.cursor = guiTextBoxState.select; - - if (guiTextBoxState.start > guiTextBoxState.cursor) - { - guiTextBoxState.start = guiTextBoxState.cursor; - guiTextBoxState.index = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.start); // Recalculate byte index - } - } - } - else - { - // Move cursor to the left - MoveTextBoxCursorLeft(text); - } - - guiTextBoxState.select = -1; - } - - framesCounter = 0; - } - else if (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0)) - { - GuiTextBoxDelete(text, length, true); - } - else if (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0)) - { - GuiTextBoxDelete(text, length, false); - } - else if (IsKeyPressed(KEY_HOME)) - { - if (IsKeyDown(KEY_LEFT_SHIFT)) - { - // Select from start of text to cursor - if ((guiTextBoxState.select > guiTextBoxState.cursor) || - ((guiTextBoxState.select == -1) && (guiTextBoxState.cursor != 0))) - { - guiTextBoxState.select = guiTextBoxState.cursor; - } - } - else guiTextBoxState.select = -1; // Deselect everything - - // Move cursor to start of text - guiTextBoxState.cursor = guiTextBoxState.start = guiTextBoxState.index = 0; - framesCounter = 0; - } - else if (IsKeyPressed(KEY_END)) - { - int max = GuiCountCodepointsUntilNewline(text); - - if (IsKeyDown(KEY_LEFT_SHIFT)) - { - if ((guiTextBoxState.select == -1) && (guiTextBoxState.cursor != max)) - { - guiTextBoxState.select = guiTextBoxState.cursor; - } - } - else guiTextBoxState.select = -1; // Deselect everything - - int pos = 0; - guiTextBoxState.cursor = max; - int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - guiTextBoxState.index = GuiMeasureTextBoxRev(text, len, textRec, &pos); - guiTextBoxState.start = guiTextBoxState.cursor - pos + 1; - } - else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_A)) - { - // `CTRL + A` Select all - GuiTextBoxSelectAll(text); - } - else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) - { - // `CTRL + C` Copy selected text to clipboard - GuiTextBoxCopy(text); - } - else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_X)) - { - // `CTRL + X` Cut selected text - GuiTextBoxCut(text); - } - else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V)) - { - // `CTRL + V` Paste clipboard text - GuiTextBoxPaste(text, textSize); - } - else if (IsKeyPressed(KEY_ENTER)) - { - pressed = true; - } - else - { - int key = GetKeyPressed(); - if ((key >= 32) && ((guiTextBoxState.cursor + 1) < textSize)) - { - if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) - { - // Delete selection - GuiTextBoxDelete(text, length, true); - } - - // Decode codepoint - char out[5] = {0}; - int sz = EncodeCodepoint(key, &out[0]); - - if (sz != 0) - { - int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); - int endIdx = startIdx + sz; - - if (endIdx <= textSize && length < textSize - 1) - { - guiTextBoxState.cursor++; - guiTextBoxState.select = -1; - memmove(&text[endIdx], &text[startIdx], length - startIdx); - memcpy(&text[startIdx], &out[0], sz); - length += sz; - text[length] = '\0'; - - if (guiTextBoxState.start != -1) - { - const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); - - if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) guiTextBoxState.start = -1; - } - } - } - } - } - - // ------------- - // HANDLE MOUSE - // ------------- - if (CheckCollisionPointRec(mousePoint, bounds)) - { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) - { - if (CheckCollisionPointRec(mousePoint, textRec)) - { - GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, &guiTextBoxState.cursor); - guiTextBoxState.cursor += guiTextBoxState.start; - guiTextBoxState.select = -1; - } - else - { - // Clicked outside the `textRec` but still inside bounds - if (mousePoint.x <= bounds.x+bounds.width/2) guiTextBoxState.cursor = 0 + guiTextBoxState.start; - else guiTextBoxState.cursor = guiTextBoxState.start + GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); - guiTextBoxState.select = -1; - } - } - else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) - { - int cursor = guiTextBoxState.cursor - guiTextBoxState.start; - bool move = false; - if (CheckCollisionPointRec(mousePoint, textRec)) - { - GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, &cursor); - } - else - { - // Clicked outside the `textRec` but still inside bounds, this means that we must move the text - move = true; - if (mousePoint.x > bounds.x+bounds.width/2) - { - cursor = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); - } - } - - guiTextBoxState.cursor = cursor + guiTextBoxState.start; - - if (guiTextBoxState.select == -1) - { - // Mark start of selection - guiTextBoxState.select = guiTextBoxState.cursor; - } - - // Move the text when cursor is positioned before or after the text - if ((framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0 && move) - { - if (cursor == 0) MoveTextBoxCursorLeft(text); - else if (cursor == GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec)) - { - MoveTextBoxCursorRight(text, length, textRec); - } - } - } - } - - // Calculate X coordinate of the blinking cursor - cursorPos.x = GuiTextBoxGetCursorCoordinates(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, guiTextBoxState.cursor - guiTextBoxState.start); + int key = GetCharPressed(); // Returns codepoint as Unicode + int keyCount = strlen(text); - // Update variables - textStartIndex = guiTextBoxState.index; - - if (guiTextBoxState.select == -1) + // Only allow keys in range [32..125] + if (keyCount < (textSize - 1)) + { + int maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)*2)); + + if ((GetTextWidth(text) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) && (key >= 32)) { - selStart = guiTextBoxState.cursor; - selLength = 0; + int byteLength = 0; + const char *textUtf8 = CodepointToUtf8(key, &byteLength); + + for (int i = 0; i < byteLength; i++) + { + text[keyCount] = textUtf8[i]; + keyCount++; + } + + text[keyCount] = '\0'; } - else if (guiTextBoxState.cursor > guiTextBoxState.select) - { - selStart = guiTextBoxState.select; - selLength = guiTextBoxState.cursor - guiTextBoxState.select; - } - else - { - selStart = guiTextBoxState.cursor; - selLength = guiTextBoxState.select - guiTextBoxState.cursor; - } - - // We aren't drawing all of the text so make sure `DrawTextRecEx()` is selecting things correctly - if (guiTextBoxState.start > selStart) - { - selLength -= guiTextBoxState.start - selStart; - selStart = 0; - } - else selStart = selStart - guiTextBoxState.start; } - else state = GUI_STATE_FOCUSED; + + // Delete text + if (keyCount > 0) + { + if (IsKeyPressed(KEY_BACKSPACE)) + { + keyCount--; + text[keyCount] = '\0'; + framesCounter = 0; + if (keyCount < 0) keyCount = 0; + } + else if (IsKeyDown(KEY_BACKSPACE)) + { + if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--; + text[keyCount] = '\0'; + if (keyCount < 0) keyCount = 0; + } + } + + if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true; + + // Check text alignment to position cursor properly + int textAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); + if (textAlignment == GUI_TEXT_ALIGN_CENTER) cursor.x = bounds.x + GetTextWidth(text)/2 + bounds.width/2 + 1; + else if (textAlignment == GUI_TEXT_ALIGN_RIGHT) cursor.x = bounds.x + bounds.width - GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING); } else { if (CheckCollisionPointRec(mousePoint, bounds)) { state = GUI_STATE_FOCUSED; - if (IsMouseButtonPressed(0)) pressed = true; - } - - if (active && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) - { - // If active copy all text to clipboard even when disabled - - // Backup textbox state - int select = guiTextBoxState.select; - int cursor = guiTextBoxState.cursor; - int start = guiTextBoxState.start; - if (guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor) - { - // If no selection then mark all text to be copied to clipboard - GuiTextBoxSelectAll(text); - } - - GuiTextBoxCopy(text); - - // Restore textbox state - guiTextBoxState.select = select; - guiTextBoxState.cursor = cursor; - guiTextBoxState.start = start; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; } } + + if (pressed) framesCounter = 0; } - + //-------------------------------------------------------------------- + // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); - if (state == GUI_STATE_PRESSED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); - if (editMode && active && ((framesCounter/TEXTEDIT_CURSOR_BLINK_FRAMES)%2 == 0) && selLength == 0) - { - // Draw the blinking cursor - DrawRectangle(cursorPos.x, cursorPos.y, 1, GuiGetStyle(DEFAULT, TEXT_SIZE), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); - } + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + + // Draw blinking cursor + if (editMode && ((framesCounter/20)%2 == 0)) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); } else if (state == GUI_STATE_DISABLED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); } - - // Finally draw the text and selection - DrawTextRecEx(guiFont, &text[textStartIndex], textRec, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), false, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha), selStart, selLength, GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_FG)), GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_BG))); - + else GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); + + GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + return pressed; } -#else // !RAYGUI_TEXTBOX_EXTENDED // Spinner control, returns selected value -// NOTE: Requires static variables: framesCounter, valueSpeed - ERROR! -RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) +bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) { + GuiControlState state = guiState; + bool pressed = false; int tempValue = *value; - Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING), bounds.y, - bounds.width - 2*(GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING)), bounds.height }; - Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.height }; - Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.height }; + Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_PADDING), bounds.y, + bounds.width - 2*(GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_PADDING)), bounds.height }; + Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height }; + Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height }; + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = GetTextWidth(text); + textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING); + } // Update control //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check spinner state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + } + } + if (!editMode) { if (tempValue < minValue) tempValue = minValue; @@ -2528,17 +1543,16 @@ RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxVal // Draw control //-------------------------------------------------------------------- // TODO: Set Spinner properties for ValueBox - pressed = GuiValueBox(spinner, &tempValue, minValue, maxValue, editMode); + pressed = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode); // Draw value selector custom buttons // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); - GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH)); - int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH)); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); -#if defined(RAYGUI_RICONS_SUPPORT) +#if defined(RAYGUI_SUPPORT_ICONS) if (GuiButton(leftButtonBound, GuiIconText(RICON_ARROW_LEFT_FILL, NULL))) tempValue--; if (GuiButton(rightButtonBound, GuiIconText(RICON_ARROW_RIGHT_FILL, NULL))) tempValue++; #else @@ -2548,6 +1562,9 @@ RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxVal GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + + // Draw text label if provided + if (text != NULL) GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT)? GUI_TEXT_ALIGN_LEFT : GUI_TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- *value = tempValue; @@ -2556,17 +1573,29 @@ RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxVal // Value Box control, updates input text with numbers // NOTE: Requires static variables: framesCounter -RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) +bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) { - #define VALUEBOX_MAX_CHARS 32 + #if !defined(VALUEBOX_MAX_CHARS) + #define VALUEBOX_MAX_CHARS 32 + #endif static int framesCounter = 0; // Required for blinking cursor GuiControlState state = guiState; bool pressed = false; - char text[VALUEBOX_MAX_CHARS + 1] = "\0"; - sprintf(text, "%i", *value); + char textValue[VALUEBOX_MAX_CHARS + 1] = "\0"; + sprintf(textValue, "%i", *value); + + Rectangle textBounds = { 0 }; + if (text != NULL) + { + textBounds.width = GetTextWidth(text); + textBounds.height = 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) == GUI_TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING); + } // Update control //-------------------------------------------------------------------- @@ -2582,18 +1611,18 @@ RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxVa framesCounter++; - int keyCount = strlen(text); + int keyCount = strlen(textValue); // Only allow keys in range [48..57] if (keyCount < VALUEBOX_MAX_CHARS) { - int maxWidth = (bounds.width - (GuiGetStyle(VALUEBOX, INNER_PADDING)*2)); - if (GetTextWidth(text) < maxWidth) + int maxWidth = bounds.width; + if (GetTextWidth(textValue) < maxWidth) { - int key = GetKeyPressed(); + int key = GetCharPressed(); if ((key >= 48) && (key <= 57)) { - text[keyCount] = (char)key; + textValue[keyCount] = (char)key; keyCount++; valueHasChanged = true; } @@ -2606,7 +1635,7 @@ RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxVa if (IsKeyPressed(KEY_BACKSPACE)) { keyCount--; - text[keyCount] = '\0'; + textValue[keyCount] = '\0'; framesCounter = 0; if (keyCount < 0) keyCount = 0; valueHasChanged = true; @@ -2614,32 +1643,27 @@ RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxVa else if (IsKeyDown(KEY_BACKSPACE)) { if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--; - text[keyCount] = '\0'; + textValue[keyCount] = '\0'; if (keyCount < 0) keyCount = 0; valueHasChanged = true; } } - if (valueHasChanged) *value = atoi(text); + if (valueHasChanged) *value = TextToInteger(textValue); + + if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true; } else { if (*value > maxValue) *value = maxValue; else if (*value < minValue) *value = minValue; - } - if (!editMode) - { if (CheckCollisionPointRec(mousePoint, bounds)) { state = GUI_STATE_FOCUSED; - if (IsMouseButtonPressed(0)) pressed = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; } } - else - { - if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) pressed = true; - } if (pressed) framesCounter = 0; } @@ -2647,136 +1671,49 @@ RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxVa // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha)); + Color baseColor = BLANK; + if (state == GUI_STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); + else if (state == GUI_STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); + + // WARNING: BLANK color does not work properly with Fade() + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha)); - if (state == GUI_STATE_PRESSED) + // Draw blinking cursor + if ((state == GUI_STATE_PRESSED) && (editMode && ((framesCounter/20)%2 == 0))) { - DrawRectangle(bounds.x + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)), guiAlpha)); - - if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GetTextWidth(text)/2 + bounds.width/2 + 2, bounds.y + GuiGetStyle(VALUEBOX, INNER_PADDING), 1, bounds.height - GuiGetStyle(VALUEBOX, INNER_PADDING)*2, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + // NOTE: ValueBox internal text is always centered + Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 2, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 1, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) }; + GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); } - else if (state == GUI_STATE_DISABLED) - { - DrawRectangle(bounds.x + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)), guiAlpha)); - } - - GuiDrawText(text, GetTextBounds(VALUEBOX, bounds), GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha)); + + // Draw text label if provided + if (text != NULL) GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT)? GUI_TEXT_ALIGN_LEFT : GUI_TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- return pressed; } -// Text Box control, updates input text -// NOTE 1: Requires static variables: framesCounter -// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) -RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) -{ - static int framesCounter = 0; // Required for blinking cursor - - GuiControlState state = guiState; - bool pressed = false; - - // Update control - //-------------------------------------------------------------------- - if ((state != GUI_STATE_DISABLED) && !guiLocked) - { - Vector2 mousePoint = GetMousePosition(); - - if (editMode) - { - state = GUI_STATE_PRESSED; - framesCounter++; - - int key = GetKeyPressed(); - int keyCount = strlen(text); - - // Only allow keys in range [32..125] - if (keyCount < (textSize - 1)) - { - int maxWidth = (bounds.width - (GuiGetStyle(DEFAULT, INNER_PADDING)*2)); - - if (GetTextWidth(text) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) - { - if (((key >= 32) && (key <= 125)) || - ((key >= 128) && (key < 255))) - { - text[keyCount] = (char)key; - keyCount++; - text[keyCount] = '\0'; - } - } - } - - // Delete text - if (keyCount > 0) - { - if (IsKeyPressed(KEY_BACKSPACE)) - { - keyCount--; - text[keyCount] = '\0'; - framesCounter = 0; - if (keyCount < 0) keyCount = 0; - } - else if (IsKeyDown(KEY_BACKSPACE)) - { - if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--; - text[keyCount] = '\0'; - if (keyCount < 0) keyCount = 0; - } - } - } - - if (!editMode) - { - if (CheckCollisionPointRec(mousePoint, bounds)) - { - state = GUI_STATE_FOCUSED; - if (IsMouseButtonPressed(0)) pressed = true; - } - } - else - { - if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) pressed = true; - } - - if (pressed) framesCounter = 0; - } - //-------------------------------------------------------------------- - - // Draw control - //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); - - if (state == GUI_STATE_PRESSED) - { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); - - // Draw blinking cursor - if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, INNER_PADDING) + GetTextWidth(text) + 2 + bounds.width/2*GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), 1, GuiGetStyle(DEFAULT, TEXT_SIZE)*2, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); - } - else if (state == GUI_STATE_DISABLED) - { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); - } - - GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); - //-------------------------------------------------------------------- - - return pressed; -} -#endif - // Text Box control with multiple lines -RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) +bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) { static int framesCounter = 0; // Required for blinking cursor GuiControlState state = guiState; bool pressed = false; - bool textHasChange = false; + Rectangle textAreaBounds = { + bounds.x + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), + bounds.y + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), + bounds.width - 2*GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), + bounds.height - 2*GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) + }; + + // Cursor position, [x, y] values should be updated + Rectangle cursor = { 0, 0, 1, GuiGetStyle(DEFAULT, TEXT_SIZE) + 2 }; + + int textWidth = 0; int currentLine = 0; - //const char *numChars = NULL; // Update control //-------------------------------------------------------------------- @@ -2787,48 +1724,32 @@ RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool if (editMode) { state = GUI_STATE_PRESSED; - framesCounter++; + int character = GetCharPressed(); int keyCount = strlen(text); - int maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2)); - int maxHeight = (bounds.height - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2)); - //numChars = TextFormat("%i/%i", keyCount, textSize - 1); - - // Only allow keys in range [32..125] + // Introduce characters if (keyCount < (textSize - 1)) { - int key = GetKeyPressed(); + Vector2 textSize = MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)); - if (MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), 1).y < (maxHeight - GuiGetStyle(DEFAULT, TEXT_SIZE))) + if (textSize.y < (textAreaBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE))) { if (IsKeyPressed(KEY_ENTER)) { text[keyCount] = '\n'; keyCount++; } - else if (((key >= 32) && (key <= 125)) || - ((key >= 128) && (key < 255))) + else if (((character >= 32) && (character < 255))) // TODO: Support Unicode inputs { - text[keyCount] = (char)key; + text[keyCount] = (char)character; keyCount++; - textHasChange = true; - } - } - else if (GetTextWidth(strrchr(text, '\n')) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) - { - if (((key >= 32) && (key <= 125)) || - ((key >= 128) && (key < 255))) - { - text[keyCount] = (char)key; - keyCount++; - textHasChange = true; } } } - // Delete text + // Delete characters if (keyCount > 0) { if (IsKeyPressed(KEY_BACKSPACE)) @@ -2836,131 +1757,76 @@ RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool keyCount--; text[keyCount] = '\0'; framesCounter = 0; + if (keyCount < 0) keyCount = 0; - textHasChange = true; } else if (IsKeyDown(KEY_BACKSPACE)) { if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--; text[keyCount] = '\0'; + if (keyCount < 0) keyCount = 0; - textHasChange = true; } } - // Introduce automatic new line if necessary - if (textHasChange) + // Calculate cursor position considering text + char oneCharText[2] = { 0 }; + int lastBreakingPos = -1; + + for (int i = 0; i < keyCount && currentLine < keyCount; i++) { - textHasChange = false; + oneCharText[0] = text[i]; + textWidth += (GetTextWidth(oneCharText) + GuiGetStyle(DEFAULT, TEXT_SPACING)); - char *lastLine = strrchr(text, '\n'); - int maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2)); + if (text[i] == ' ' || text[i] == '\n') lastBreakingPos = i; - if (lastLine != NULL) + if ( text[i] == '\n' || textWidth >= textAreaBounds.width) { - if (GetTextWidth(lastLine) > maxWidth) - { - int firstIndex = lastLine - text; + currentLine++; + textWidth = 0; - char *lastSpace = strrchr(lastLine, 32); + if (lastBreakingPos > 0) i = lastBreakingPos; + else textWidth += (GetTextWidth(oneCharText) + GuiGetStyle(DEFAULT, TEXT_SPACING)); - if (lastSpace != NULL) - { - int secondIndex = lastSpace - lastLine; - text[firstIndex + secondIndex] = '\n'; - } - else - { - int len = (lastLine != NULL)? strlen(lastLine) : 0; - char lastChar = lastLine[len - 1]; - lastLine[len - 1] = '\n'; - lastLine[len] = lastChar; - lastLine[len + 1] = '\0'; - keyCount++; - } - } - } - else - { - if (GetTextWidth(text) > maxWidth) - { - char *lastSpace = strrchr(text, 32); - - if (lastSpace != NULL) - { - int index = lastSpace - text; - text[index] = '\n'; - } - else - { - int len = (lastLine != NULL)? strlen(lastLine) : 0; - char lastChar = lastLine[len - 1]; - lastLine[len - 1] = '\n'; - lastLine[len] = lastChar; - lastLine[len + 1] = '\0'; - keyCount++; - } - } + lastBreakingPos = -1; } } - // Counting how many new lines - for (int i = 0; i < keyCount; i++) - { - if (text[i] == '\n') currentLine++; - } + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING) + textWidth - GuiGetStyle(DEFAULT, TEXT_SPACING); + cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)/2 + ((GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING))*currentLine); + + // Exit edit mode + if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; } - - // Changing edit mode - if (!editMode) + else { if (CheckCollisionPointRec(mousePoint, bounds)) { state = GUI_STATE_FOCUSED; - if (IsMouseButtonPressed(0)) pressed = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; } } - else - { - if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0)) pressed = true; - } - if (pressed) framesCounter = 0; + if (pressed) framesCounter = 0; // Reset blinking cursor } //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); - if (state == GUI_STATE_PRESSED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); - if (editMode) - { - if ((framesCounter/20)%2 == 0) - { - char *line = NULL; - if (currentLine > 0) line = strrchr(text, '\n'); - else line = text; - - // Draw text cursor - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING) + GetTextWidth(line), - bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING)/2 + ((GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(TEXTBOX, INNER_PADDING))*currentLine), - 1, GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(TEXTBOX, INNER_PADDING), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_FOCUSED)), guiAlpha)); - } - - // Draw characters counter - //GuiDrawText(numChars, RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GetTextWidth(numChars) - GuiGetStyle(TEXTBOX, INNER_PADDING), bounds.y + bounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE) - GuiGetStyle(TEXTBOX, INNER_PADDING) }, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT_COLOR_PRESSED)), guiAlpha/2)); - } + // Draw blinking cursor + if (editMode && ((framesCounter/20)%2 == 0)) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); } else if (state == GUI_STATE_DISABLED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); } + else GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); - GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + DrawTextRec(guiFont, text, textAreaBounds, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), true, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- return pressed; @@ -2968,14 +1834,14 @@ RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool // Slider control with pro parameters // NOTE: Other GuiSlider*() controls use this one -RAYGUIDEF float GuiSliderPro(Rectangle bounds, const char *text, float value, float minValue, float maxValue, int sliderWidth, bool showValue) +float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue, int sliderWidth) { GuiControlState state = guiState; int sliderValue = (int)(((value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); - Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, INNER_PADDING), - 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, INNER_PADDING) }; + 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) }; if (sliderWidth > 0) // Slider { @@ -2988,12 +1854,6 @@ RAYGUIDEF float GuiSliderPro(Rectangle bounds, const char *text, float value, fl slider.width = sliderValue; } - Rectangle textBounds = { 0 }; - textBounds.width = GetTextWidth(text); // TODO: Consider text icon - textBounds.height = 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; - // Update control //-------------------------------------------------------------------- if ((state != GUI_STATE_DISABLED) && !guiLocked) @@ -3033,45 +1893,59 @@ RAYGUIDEF float GuiSliderPro(Rectangle bounds, const char *text, float value, fl // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha)); - DrawRectangle(bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + // Draw slider internal bar (depends on state) - if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == GUI_STATE_FOCUSED) DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == GUI_STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); - GuiDrawText(text, textBounds, GuiGetStyle(SLIDER, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = GetTextWidth(textLeft); // TODO: Consider text icon + textBounds.height = 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; - // TODO: Review showValue parameter, really ugly... - if (showValue) GuiDrawText(TextFormat("%.02f", value), RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING), - (float)bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING), - (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, GUI_TEXT_ALIGN_LEFT, - Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textLeft, textBounds, GUI_TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = GetTextWidth(textRight); // TODO: Consider text icon + textBounds.height = 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, GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + } //-------------------------------------------------------------------- return value; } // Slider control extended, returns selected value and has text -RAYGUIDEF float GuiSlider(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue) +float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) { - return GuiSliderPro(bounds, text, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH), showValue); + return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH)); } // Slider Bar control extended, returns selected value -RAYGUIDEF float GuiSliderBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue) +float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) { - return GuiSliderPro(bounds, text, value, minValue, maxValue, 0, showValue); + return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0); } // Progress Bar control extended, shows current progress value -RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue) +float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) { GuiControlState state = guiState; Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), - bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, INNER_PADDING), 0, - bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, INNER_PADDING) }; + bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0, + bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) }; // Update control //-------------------------------------------------------------------- @@ -3080,34 +1954,54 @@ RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, // Draw control //-------------------------------------------------------------------- - if (showValue) GuiLabel(RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING), (float)bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING), (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, TextFormat("%.02f", value)); + GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha), BLANK); - DrawRectangleLinesEx(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha)); - // Draw slider internal progress bar (depends on state) - if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == GUI_STATE_FOCUSED) DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha)); + if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == GUI_STATE_FOCUSED) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha)); + + // Draw left/right text if provided + if (textLeft != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = GetTextWidth(textLeft); // TODO: Consider text icon + textBounds.height = 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, GUI_TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + } + + if (textRight != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = GetTextWidth(textRight); // TODO: Consider text icon + textBounds.height = 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, GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + } //-------------------------------------------------------------------- return value; } // Status Bar control -RAYGUIDEF void GuiStatusBar(Rectangle bounds, const char *text) +void GuiStatusBar(Rectangle bounds, const char *text) { GuiControlState state = guiState; // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha)); - DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 }, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - - GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != GUI_STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha), + Fade(GetColor(GuiGetStyle(STATUSBAR, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != GUI_STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); //-------------------------------------------------------------------- } // Dummy rectangle control, intended for placeholding -RAYGUIDEF void GuiDummyRec(Rectangle bounds, const char *text) +void GuiDummyRec(Rectangle bounds, const char *text) { GuiControlState state = guiState; @@ -3128,14 +2022,14 @@ RAYGUIDEF void GuiDummyRec(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - + GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != GUI_STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); //------------------------------------------------------------------ } // Scroll Bar control -RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) +// TODO: I feel GuiScrollBar could be simplified... +int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) { GuiControlState state = guiState; @@ -3148,10 +2042,10 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal // 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 }; @@ -3160,7 +2054,7 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal if (value < minValue) value = minValue; const int range = maxValue - minValue; - int sliderSize = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); + int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_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 }; @@ -3168,16 +2062,16 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal 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, INNER_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; + 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) }; sliderSize = (sliderSize >= scrollbar.height)? (scrollbar.height - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING), (float)scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)), (float)bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)), (float)sliderSize }; + slider = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)), (float)bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), (float)sliderSize }; } else { 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, INNER_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING))}; + 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))}; sliderSize = (sliderSize >= scrollbar.width)? (scrollbar.width - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = RAYGUI_CLITERAL(Rectangle){ (float)scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING), (float)sliderSize, (float)bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)) }; + slider = RAYGUI_CLITERAL(Rectangle){ (float)scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)sliderSize, (float)bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) }; } // Update control @@ -3224,12 +2118,10 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal // Draw control //-------------------------------------------------------------------- - DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background - DrawRectangleRec(scrollbar, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background - - DrawRectangleLinesEx(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha)); - - DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar + GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background + + GuiDrawRectangle(scrollbar, 0, BLANK, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background + GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar // Draw arrows const int padding = (spinnerSize - GuiGetStyle(SCROLLBAR, ARROWS_SIZE))/2; @@ -3276,12 +2168,43 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal return value; } -// List Element control, returns element state -static bool GuiListElement(Rectangle bounds, const char *text, bool active, bool editMode) +// List View control +int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active) +{ + int itemsCount = 0; + const char **items = NULL; + + if (text != NULL) items = GuiTextSplit(text, &itemsCount, NULL); + + return GuiListViewEx(bounds, items, itemsCount, NULL, scrollIndex, active); +} + +// List View control with extended parameters +int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, int *scrollIndex, int active) { GuiControlState state = guiState; + int itemFocused = (focus == NULL)? -1 : *focus; + int itemSelected = active; - if (!guiLocked && editMode) state = GUI_STATE_NORMAL; + // Check if we need a scroll bar + bool useScrollBar = false; + if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING))*count > bounds.height) useScrollBar = true; + + // Define base item rectangle [0] + Rectangle itemBounds = { 0 }; + itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING); + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.height = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); + if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH); + + // Get items on the list + int visibleItems = bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); + 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 //-------------------------------------------------------------------- @@ -3289,346 +2212,118 @@ static bool GuiListElement(Rectangle bounds, const char *text, bool active, bool { Vector2 mousePoint = GetMousePosition(); + // Check mouse inside list view if (CheckCollisionPointRec(mousePoint, bounds)) { - if (!active) + state = GUI_STATE_FOCUSED; + + // Check focused and selected item + for (int i = 0; i < visibleItems; i++) { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; - else state = GUI_STATE_FOCUSED; - } - - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) active = !active; - } - } - //-------------------------------------------------------------------- - - // Draw control - //-------------------------------------------------------------------- - // Draw element rectangle - switch (state) - { - case GUI_STATE_NORMAL: - { - if (active) - { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha)); - } - } break; - case GUI_STATE_FOCUSED: - { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha)); - } break; - case GUI_STATE_PRESSED: - { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha)); - } break; - case GUI_STATE_DISABLED: - { - if (active) - { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), guiAlpha)); - } - } break; - default: break; - } - - // Draw text depending on state - if (state == GUI_STATE_NORMAL) GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, active? TEXT_COLOR_PRESSED : TEXT_COLOR_NORMAL)), guiAlpha)); - else if (state == GUI_STATE_DISABLED) GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, active? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); - else GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + state*3)), guiAlpha)); - //-------------------------------------------------------------------- - - return active; -} - -// List View control -RAYGUIDEF bool GuiListView(Rectangle bounds, const char *text, int *active, int *scrollIndex, bool editMode) -{ - bool result = 0; - - int count = 0; - const char **textList = GuiTextSplit(text, &count, NULL); - - result = GuiListViewEx(bounds, textList, count, NULL, active, NULL, scrollIndex, editMode); - - return result; -} - -// List View control extended parameters -// NOTE: Elements could be disabled individually and focused element could be obtained: -// int *enabled defines an array with enabled elements inside the list -// int *focus returns focused element (may be not pressed) -RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int *enabled, int *active, int *focus, int *scrollIndex, bool editMode) -{ - GuiControlState state = guiState; - bool pressed = false; - - int focusElement = -1; - int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex; - bool useScrollBar = true; - bool pressedKey = false; - - int visibleElements = bounds.height/(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)); - if ((startIndex < 0) || (startIndex > count - visibleElements)) startIndex = 0; - int endIndex = startIndex + visibleElements; - - int auxActive = *active; - - float barHeight = bounds.height; - float minBarHeight = 10; - - // Update control - //-------------------------------------------------------------------- - // All the elements fit inside ListView and dont need scrollbar. - if (visibleElements >= count) - { - useScrollBar = false; - startIndex = 0; - endIndex = count; - } - - // Calculate position X and width to draw each element. - int posX = bounds.x + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING); - int elementWidth = bounds.width - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); - - if (useScrollBar) - { - posX = GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE? posX + GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : posX; - elementWidth = bounds.width - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); - } - - Rectangle scrollBarRect = { (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), (float)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; - - if (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_RIGHT_SIDE) scrollBarRect.x = posX + elementWidth + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING); - - // Area without the scrollbar - Rectangle viewArea = { (float)posX, (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)elementWidth, (float)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; - - if ((state != GUI_STATE_DISABLED) && !guiLocked) // && !guiLocked - { - Vector2 mousePoint = GetMousePosition(); - - if (editMode) - { - state = GUI_STATE_PRESSED; - - // Change active with keys - if (IsKeyPressed(KEY_UP)) - { - if (auxActive > 0) + if (CheckCollisionPointRec(mousePoint, itemBounds)) { - auxActive--; - if ((useScrollBar) && (auxActive < startIndex)) startIndex--; + itemFocused = startIndex + i; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + if (itemSelected == (startIndex + i)) itemSelected = -1; + else itemSelected = startIndex + i; + } + break; } - pressedKey = true; - } - else if (IsKeyPressed(KEY_DOWN)) - { - if (auxActive < count - 1) - { - auxActive++; - if ((useScrollBar) && (auxActive >= endIndex)) startIndex++; - } - - pressedKey = true; + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); } if (useScrollBar) { - endIndex = startIndex + visibleElements; - if (CheckCollisionPointRec(mousePoint, viewArea)) - { - int wheel = GetMouseWheelMove(); - - if (wheel < 0 && endIndex < count) startIndex -= wheel; - else if (wheel > 0 && startIndex > 0) startIndex -= wheel; - } - - if (pressedKey) - { - pressedKey = false; - if ((auxActive < startIndex) || (auxActive >= endIndex)) startIndex = auxActive; - } + int wheelMove = GetMouseWheelMove(); + startIndex -= wheelMove; if (startIndex < 0) startIndex = 0; - else if (startIndex > (count - (endIndex - startIndex))) - { - startIndex = count - (endIndex - startIndex); - } - - endIndex = startIndex + visibleElements; + else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; + endIndex = startIndex + visibleItems; if (endIndex > count) endIndex = count; } } + else itemFocused = -1; - if (!editMode) - { - if (CheckCollisionPointRec(mousePoint, viewArea)) - { - state = GUI_STATE_FOCUSED; - if (IsMouseButtonPressed(0)) pressed = true; - - startIndex -= GetMouseWheelMove(); - - if (startIndex < 0) startIndex = 0; - else if (startIndex > (count - (endIndex - startIndex))) - { - startIndex = count - (endIndex - startIndex); - } - - pressed = true; - } - } - else - { - if (!CheckCollisionPointRec(mousePoint, viewArea)) - { - if (IsMouseButtonPressed(0) || (GetMouseWheelMove() != 0)) pressed = true; - } - } - - // Get focused element - for (int i = startIndex; i < endIndex; i++) - { - if (CheckCollisionPointRec(mousePoint, RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) })) - { - focusElement = i; - } - } - } - - const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); // Save default slider size - - // Calculate percentage of visible elements and apply same percentage to scrollbar - if (useScrollBar) - { - float percentVisible = (endIndex - startIndex)*100/count; - barHeight *= percentVisible/100; - - if (barHeight < minBarHeight) barHeight = minBarHeight; - else if (barHeight > bounds.height) barHeight = bounds.height; - - GuiSetStyle(SCROLLBAR, SLIDER_SIZE, barHeight); // Change slider size + // Reset item rectangle y to [0] + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); } //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background - // Draw scrollBar - if (useScrollBar) + // Draw visible items + for (int i = 0; ((i < visibleItems) && (text != NULL)); i++) { - const int scrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed - GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleElements); // Hack to make the spinner buttons work - - int index = scrollIndex != NULL? *scrollIndex : startIndex; - index = GuiScrollBar(scrollBarRect, index, 0, count - visibleElements); - - GuiSetStyle(SCROLLBAR, SCROLL_SPEED, scrollSpeed); // Reset scroll speed to default - GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider); // Reset slider size to default - - // FIXME: Quick hack to make this thing work, think of a better way - if (scrollIndex != NULL && CheckCollisionPointRec(GetMousePosition(), scrollBarRect) && IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (state == GUI_STATE_DISABLED) { - startIndex = index; - if (startIndex < 0) startIndex = 0; - if (startIndex > (count - (endIndex - startIndex))) - { - startIndex = count - (endIndex - startIndex); - } + if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha)); - endIndex = startIndex + visibleElements; - - if (endIndex > count) endIndex = count; + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha)); } + else + { + if ((startIndex + i) == itemSelected) + { + // Draw item selected + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), guiAlpha)); + } + else if ((startIndex + i) == itemFocused) + { + // Draw item focused + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), guiAlpha)); + } + else + { + // Draw item normal + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha)); + } + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); } - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha)); - - // Draw ListView states - switch (state) + if (useScrollBar) { - case GUI_STATE_NORMAL: - { - for (int i = startIndex; i < endIndex; i++) - { - if ((enabled != NULL) && (enabled[i] == 0)) - { - GuiDisable(); - GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); - GuiEnable(); - } - else if (i == auxActive) - { - GuiDisable(); - GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); - GuiEnable(); - } - else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); - } - } break; - case GUI_STATE_FOCUSED: - { - for (int i = startIndex; i < endIndex; i++) - { - if ((enabled != NULL) && (enabled[i] == 0)) - { - GuiDisable(); - GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); - GuiEnable(); - } - else if (i == auxActive) GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); - else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); - } - } break; - case GUI_STATE_PRESSED: - { - for (int i = startIndex; i < endIndex; i++) - { - if ((enabled != NULL) && (enabled[i] == 0)) - { - GuiDisable(); - GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); - GuiEnable(); - } - else if ((i == auxActive) && editMode) - { - if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, true) == false) auxActive = -1; - } - else - { - if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, true) == true) auxActive = i; - } - } - } break; - case GUI_STATE_DISABLED: - { - for (int i = startIndex; i < endIndex; i++) - { - if (i == auxActive) GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); - else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); - } - } break; - default: break; + 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, 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 (focus != NULL) *focus = itemFocused; if (scrollIndex != NULL) *scrollIndex = startIndex; - if (focus != NULL) *focus = focusElement; - *active = auxActive; - return pressed; + return itemSelected; } // Color Panel control -RAYGUIDEF Color GuiColorPanelEx(Rectangle bounds, Color color, float hue) +Color GuiColorPanelEx(Rectangle bounds, Color color, float hue) { GuiControlState state = guiState; Vector2 pickerSelector = { 0 }; @@ -3644,7 +2339,7 @@ RAYGUIDEF Color GuiColorPanelEx(Rectangle bounds, Color color, float hue) Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), (unsigned char)(255.0f*rgbHue.y), (unsigned char)(255.0f*rgbHue.z), 255 }; - + const Color colWhite = { 255, 255, 255, 255 }; const Color colBlack = { 0, 0, 0, 255 }; @@ -3692,32 +2387,33 @@ RAYGUIDEF Color GuiColorPanelEx(Rectangle bounds, Color color, float hue) DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0)); // Draw color picker: selector - DrawRectangle(pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), Fade(colWhite, guiAlpha)); + Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; + GuiDrawRectangle(selector, 0, BLANK, Fade(colWhite, guiAlpha)); } 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)); } - DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); //-------------------------------------------------------------------- return color; } -RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color) +Color GuiColorPanel(Rectangle bounds, Color color) { return GuiColorPanelEx(bounds, color, -1.0f); } // Color Bar Alpha control // NOTE: Returns alpha value normalized [0..1] -RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) +float GuiColorBarAlpha(Rectangle bounds, float alpha) { - #define COLORBARALPHA_CHECKED_SIZE 10 + #define COLORBARALPHA_CHECKED_SIZE 10 GuiControlState state = guiState; - Rectangle selector = { (float)bounds.x + alpha*bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.y - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2 }; + Rectangle selector = { (float)bounds.x + alpha*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (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 //-------------------------------------------------------------------- @@ -3751,16 +2447,13 @@ RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) { int checksX = bounds.width/COLORBARALPHA_CHECKED_SIZE; int checksY = bounds.height/COLORBARALPHA_CHECKED_SIZE; - + for (int x = 0; x < checksX; x++) { for (int y = 0; y < checksY; y++) { - DrawRectangle(bounds.x + x*COLORBARALPHA_CHECKED_SIZE, - bounds.y + y*COLORBARALPHA_CHECKED_SIZE, - COLORBARALPHA_CHECKED_SIZE, COLORBARALPHA_CHECKED_SIZE, - ((x + y)%2)? Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) : - Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha)); + Rectangle check = { bounds.x + x*COLORBARALPHA_CHECKED_SIZE, bounds.y + y*COLORBARALPHA_CHECKED_SIZE, COLORBARALPHA_CHECKED_SIZE, COLORBARALPHA_CHECKED_SIZE }; + GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) : Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha)); } } @@ -3768,10 +2461,10 @@ RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) } 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)); - DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); - + GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + // Draw alpha bar: selector - DrawRectangleRec(selector, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- return alpha; @@ -3779,10 +2472,10 @@ RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) // Color Bar Hue control // NOTE: Returns hue value normalized [0..1] -RAYGUIDEF float GuiColorBarHue(Rectangle bounds, float hue) +float GuiColorBarHue(Rectangle bounds, float hue) { GuiControlState state = guiState; - Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.y + hue/360.0f*bounds.height - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.width + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2, (float)GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT) }; + Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + hue/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) }; // Update control //-------------------------------------------------------------------- @@ -3824,19 +2517,19 @@ RAYGUIDEF float GuiColorBarHue(Rectangle bounds, float hue) if (state != GUI_STATE_DISABLED) { // Draw hue bar:color bars - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + (int)bounds.height/6 + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 2*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 3*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 4*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 5*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6 - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.y + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.y + (int)bounds.height/6 + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.y + 2*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.y + 3*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.y + 4*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.y + 5*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)/2, bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (int)bounds.height/6 - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha)); } else DrawRectangleGradientV(bounds.x, bounds.y, bounds.width, bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); - - DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + + GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); // Draw hue bar: selector - DrawRectangleRec(selector, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- return hue; @@ -3848,54 +2541,53 @@ RAYGUIDEF float GuiColorBarHue(Rectangle bounds, float hue) // Color Picker control // NOTE: It's divided in multiple controls: -// Color GuiColorPanel() - Color select panel +// Color GuiColorPanel(Rectangle bounds, Color color) // float GuiColorBarAlpha(Rectangle bounds, float alpha) // float GuiColorBarHue(Rectangle bounds, float value) // NOTE: bounds define GuiColorPanel() size -RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color) +Color GuiColorPicker(Rectangle bounds, Color color) { color = GuiColorPanel(bounds, color); - Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, BAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, BAR_WIDTH), (float)bounds.height }; + 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) }; Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ color.r/255.0f, color.g/255.0f, color.b/255.0f }); hsv.x = GuiColorBarHue(boundsHue, 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)(rgb.x*255.0f), (unsigned char)(rgb.y*255.0f), (unsigned char)(rgb.z*255.0f), color.a }; + 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 color; } // Message Box control -RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const char *message, const char *buttons) +int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons) { - #define MESSAGEBOX_BUTTON_HEIGHT 24 - #define MESSAGEBOX_BUTTON_PADDING 10 + #define MESSAGEBOX_BUTTON_HEIGHT 24 + #define MESSAGEBOX_BUTTON_PADDING 10 int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button int buttonsCount = 0; const char **buttonsText = GuiTextSplit(buttons, &buttonsCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + MESSAGEBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - MESSAGEBOX_BUTTON_HEIGHT - MESSAGEBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - MESSAGEBOX_BUTTON_PADDING*(buttonsCount + 1))/buttonsCount; + buttonBounds.height = MESSAGEBOX_BUTTON_HEIGHT; Vector2 textSize = MeasureTextEx(guiFont, message, GuiGetStyle(DEFAULT, TEXT_SIZE), 1); Rectangle textBounds = { 0 }; textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; - textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + (bounds.height - WINDOW_STATUSBAR_HEIGHT)/4 - textSize.y/2; + textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + (bounds.height - WINDOW_STATUSBAR_HEIGHT - MESSAGEBOX_BUTTON_HEIGHT - MESSAGEBOX_BUTTON_PADDING)/2 - textSize.y/2; textBounds.width = textSize.x; textBounds.height = textSize.y; - Rectangle buttonBounds = { 0 }; - buttonBounds.x = bounds.x + MESSAGEBOX_BUTTON_PADDING; - buttonBounds.y = bounds.y + bounds.height/2 + bounds.height/4 - MESSAGEBOX_BUTTON_HEIGHT/2; - buttonBounds.width = (bounds.width - MESSAGEBOX_BUTTON_PADDING*(buttonsCount + 1))/buttonsCount; - buttonBounds.height = MESSAGEBOX_BUTTON_HEIGHT; - // Draw control //-------------------------------------------------------------------- - if (GuiWindowBox(bounds, windowTitle)) clicked = 0; + if (GuiWindowBox(bounds, title)) clicked = 0; int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); @@ -3918,12 +2610,76 @@ RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const cha } // Text Input Box control, ask for text -RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *windowTitle, const char *message, char *text, const char *buttons) +int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text) { + #define TEXTINPUTBOX_BUTTON_HEIGHT 24 + #define TEXTINPUTBOX_BUTTON_PADDING 10 + #define TEXTINPUTBOX_HEIGHT 30 + + #define TEXTINPUTBOX_MAX_TEXT_LENGTH 256 + + // Used to enable text edit mode + // WARNING: No more than one GuiTextInputBox() should be open at the same time + static bool textEditMode = false; + int btnIndex = -1; - - // TODO: GuiTextInputBox() - + + int buttonsCount = 0; + const char **buttonsText = GuiTextSplit(buttons, &buttonsCount, NULL); + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height - TEXTINPUTBOX_BUTTON_HEIGHT - TEXTINPUTBOX_BUTTON_PADDING; + buttonBounds.width = (bounds.width - TEXTINPUTBOX_BUTTON_PADDING*(buttonsCount + 1))/buttonsCount; + buttonBounds.height = TEXTINPUTBOX_BUTTON_HEIGHT; + + int messageInputHeight = bounds.height - WINDOW_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - TEXTINPUTBOX_BUTTON_HEIGHT - 2*TEXTINPUTBOX_BUTTON_PADDING; + + Rectangle textBounds = { 0 }; + if (message != NULL) + { + Vector2 textSize = MeasureTextEx(guiFont, message, GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + + textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; + textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + messageInputHeight/4 - textSize.y/2; + textBounds.width = textSize.x; + textBounds.height = textSize.y; + } + + Rectangle textBoxBounds = { 0 }; + textBoxBounds.x = bounds.x + TEXTINPUTBOX_BUTTON_PADDING; + textBoxBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT - TEXTINPUTBOX_HEIGHT/2; + if (message == NULL) textBoxBounds.y += messageInputHeight/2; + else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4); + textBoxBounds.width = bounds.width - TEXTINPUTBOX_BUTTON_PADDING*2; + textBoxBounds.height = TEXTINPUTBOX_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, title)) btnIndex = 0; + + // Draw message if available + if (message != NULL) + { + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + } + + if (GuiTextBox(textBoxBounds, text, TEXTINPUTBOX_MAX_TEXT_LENGTH, textEditMode)) textEditMode = !textEditMode; + + int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + + for (int i = 0; i < buttonsCount; i++) + { + if (GuiButton(buttonBounds, buttonsText[i])) btnIndex = i + 1; + buttonBounds.x += (buttonBounds.width + MESSAGEBOX_BUTTON_PADDING); + } + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment); + //-------------------------------------------------------------------- + return btnIndex; } @@ -3931,16 +2687,18 @@ RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *windowTitle, const c // NOTE: Returns grid mouse-hover selected cell // About drawing lines at subpixel spacing, simple put, not easy solution: // https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster -RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) +Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) { - #define GRID_COLOR_ALPHA 0.15f // Grid lines alpha amount + #if !defined(GRID_COLOR_ALPHA) + #define GRID_COLOR_ALPHA 0.15f // Grid lines alpha amount + #endif GuiControlState state = guiState; Vector2 mousePoint = GetMousePosition(); Vector2 currentCell = { -1, -1 }; - int linesV = ((int)(bounds.width/spacing) + 1)*subdivs; - int linesH = ((int)(bounds.height/spacing) + 1)*subdivs; + int linesV = ((int)(bounds.width/spacing))*subdivs + 1; + int linesH = ((int)(bounds.height/spacing))*subdivs + 1; // Update control //-------------------------------------------------------------------- @@ -3960,18 +2718,22 @@ RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) { case GUI_STATE_NORMAL: { - // Draw vertical grid lines - for (int i = 0; i < linesV; i++) + if (subdivs > 0) { - DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + spacing*i, bounds.y, 1, bounds.height }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); - } + // Draw vertical grid lines + for (int i = 0; i < linesV; i++) + { + Rectangle lineV = { bounds.x + spacing * i / subdivs, bounds.y, 1, bounds.height }; + GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA * 4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); + } - // Draw horizontal grid lines - for (int i = 0; i < linesH; i++) - { - DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + spacing*i, bounds.width, 1 }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); + // Draw horizontal grid lines + for (int i = 0; i < linesH; i++) + { + Rectangle lineH = { bounds.x, bounds.y + spacing * i / subdivs, bounds.width, 1 }; + GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA * 4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); + } } - } break; default: break; } @@ -3984,10 +2746,10 @@ RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) //---------------------------------------------------------------------------------- // Load raygui style file (.rgs) -RAYGUIDEF void GuiLoadStyle(const char *fileName) +void GuiLoadStyle(const char *fileName) { bool tryBinary = false; - + // Try reading the files as text file first FILE *rgsFile = fopen(fileName, "rt"); @@ -3995,12 +2757,12 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) { char buffer[256] = { 0 }; fgets(buffer, 256, rgsFile); - + if (buffer[0] == '#') { int controlId = 0; int propertyId = 0; - int propertyValue = 0; + unsigned int propertyValue = 0; while (!feof(rgsFile)) { @@ -4008,34 +2770,46 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) { case 'p': { + // Style property: p + sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue); - - 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, propertyId, propertyValue); - - if (propertyId < NUM_PROPS_DEFAULT) for (int i = 1; i < NUM_CONTROLS; i++) GuiSetStyle(i, propertyId, propertyValue); - } - else GuiSetStyle(controlId, propertyId, propertyValue); - + + GuiSetStyle(controlId, propertyId, (int)propertyValue); + } break; case 'f': { - int fontSize = 0; - int fontSpacing = 0; - char fontFileName[256] = { 0 }; - sscanf(buffer, "f %d %d %[^\n]s", &fontSize, &fontSpacing, fontFileName); + // Style font: f - Font font = LoadFontEx(FormatText("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); - - if ((font.texture.id > 0) && (font.charsCount > 0)) + 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 }; + + if (charmapFileName[0] != '0') { - GuiFont(font); - GuiSetStyle(DEFAULT, TEXT_SIZE, fontSize); - GuiSetStyle(DEFAULT, TEXT_SPACING, fontSpacing); + // Load characters from charmap file, + // expected '\n' separated list of integer values + char *charValues = LoadText(charmapFileName); + if (charValues != NULL) + { + int charsCount = 0; + const char **chars = TextSplit(charValues, '\n', &charsCount); + + int *values = (int *)RAYGUI_MALLOC(charsCount*sizeof(int)); + for (int i = 0; i < charsCount; i++) values[i] = TextToInteger(chars[i]); + + font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, values, charsCount); + + RAYGUI_FREE(values); + } } + else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); + + if ((font.texture.id > 0) && (font.charsCount > 0)) GuiSetFont(font); + } break; default: break; } @@ -4047,12 +2821,11 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) fclose(rgsFile); } - else return; - + if (tryBinary) { rgsFile = fopen(fileName, "rb"); - + if (rgsFile == NULL) return; char signature[5] = ""; @@ -4073,7 +2846,7 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) short controlId = 0; short propertyId = 0; int propertyValue = 0; - + for (int i = 0; i < propertiesCount; i++) { fread(&controlId, 1, sizeof(short), rgsFile); @@ -4085,7 +2858,7 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) // 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 < NUM_PROPS_DEFAULT) for (int i = 1; i < NUM_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue); } else GuiSetStyle((int)controlId, (int)propertyId, propertyValue); @@ -4122,27 +2895,30 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) fread(&imFont.width, 1, sizeof(int), rgsFile); fread(&imFont.height, 1, sizeof(int), rgsFile); fread(&imFont.format, 1, sizeof(int), rgsFile); - - imFont.data = (unsigned char *)malloc(fontImageSize); + + imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageSize); fread(imFont.data, 1, fontImageSize, rgsFile); font.texture = LoadTextureFromImage(imFont); - + UnloadImage(imFont); } - // Load font chars data - font.chars = (CharInfo *)calloc(font.charsCount, sizeof(CharInfo)); + // Load font recs data + font.recs = (Rectangle *)RAYGUI_CALLOC(font.charsCount, sizeof(Rectangle)); + for (int i = 0; i < font.charsCount; i++) fread(&font.recs[i], 1, sizeof(Rectangle), rgsFile); + + // Load font chars info data + font.chars = (CharInfo *)RAYGUI_CALLOC(font.charsCount, sizeof(CharInfo)); for (int i = 0; i < font.charsCount; i++) { - fread(&font.recs[i], 1, sizeof(Rectangle), rgsFile); fread(&font.chars[i].value, 1, sizeof(int), rgsFile); fread(&font.chars[i].offsetX, 1, sizeof(int), rgsFile); fread(&font.chars[i].offsetY, 1, sizeof(int), rgsFile); fread(&font.chars[i].advanceX, 1, sizeof(int), rgsFile); } - GuiFont(font); + GuiSetFont(font); // Set font texture source rectangle to be used as white texture to draw shapes // NOTE: This way, all gui can be draw using a single draw call @@ -4155,24 +2931,8 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) } } -// Load style from a palette values array -RAYGUIDEF void GuiLoadStyleProps(const int *props, int count) -{ - int completeSets = count/(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); - int uncompleteSetProps = count%(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); - - // Load style palette values from array (complete property sets) - for (int i = 0; i < completeSets; i++) - { - for (int j = 0; j < (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); j++) GuiSetStyle(i, j, props[i]); - } - - // Load style palette values from array (uncomplete property set) - for (int k = 0; k < uncompleteSetProps; k++) GuiSetStyle(completeSets, k, props[completeSets*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + k]); -} - // Load style default over global style -RAYGUIDEF void GuiLoadStyleDefault(void) +void GuiLoadStyleDefault(void) { // We set this variable first to avoid cyclic function calls // when calling GuiSetStyle() and GuiGetStyle() @@ -4191,78 +2951,73 @@ RAYGUIDEF void GuiLoadStyleDefault(void) 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, INNER_PADDING, 1); - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); // WARNING: Some controls use other values + GuiSetStyle(DEFAULT, TEXT_PADDING, 0); // WARNING: Some controls use other values + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); // WARNING: Some controls use other values - // Populate all controls with default style - for (int i = 1; i < NUM_CONTROLS; i++) - { - for (int j = 0; j < NUM_PROPS_DEFAULT; j++) GuiSetStyle(i, j, GuiGetStyle(DEFAULT, j)); - } - - guiFont = GetFontDefault(); // Initialize default font + // Initialize control-specific property values + // NOTE: Those properties are in default list but require specific values by control type + GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 2); + GuiSetStyle(SLIDER, TEXT_PADDING, 5); + GuiSetStyle(CHECKBOX, TEXT_PADDING, 5); + GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_RIGHT); + GuiSetStyle(TEXTBOX, TEXT_PADDING, 5); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(VALUEBOX, TEXT_PADDING, 4); + GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(SPINNER, TEXT_PADDING, 4); + GuiSetStyle(SPINNER, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(STATUSBAR, TEXT_PADDING, 6); + GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); // Initialize extended property values // NOTE: By default, extended property values are initialized to 0 - GuiSetStyle(DEFAULT, TEXT_SIZE, 10); - GuiSetStyle(DEFAULT, TEXT_SPACING, 1); - GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property - GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property - - GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); - GuiSetStyle(BUTTON, BORDER_WIDTH, 2); - GuiSetStyle(BUTTON, INNER_PADDING, 4); + 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(TOGGLE, GROUP_PADDING, 2); GuiSetStyle(SLIDER, SLIDER_WIDTH, 15); - GuiSetStyle(SLIDER, TEXT_PADDING, 5); - GuiSetStyle(CHECKBOX, CHECK_TEXT_PADDING, 5); - GuiSetStyle(COMBOBOX, SELECTOR_WIDTH, 30); - GuiSetStyle(COMBOBOX, SELECTOR_PADDING, 2); - GuiSetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING, 16); - GuiSetStyle(TEXTBOX, INNER_PADDING, 4); - GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); - GuiSetStyle(TEXTBOX, MULTILINE_PADDING, 5); + GuiSetStyle(SLIDER, SLIDER_PADDING, 1); + GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1); + GuiSetStyle(CHECKBOX, CHECK_PADDING, 1); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 30); + GuiSetStyle(COMBOBOX, COMBO_BUTTON_PADDING, 2); + GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); + GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_PADDING, 2); + GuiSetStyle(TEXTBOX, TEXT_LINES_PADDING, 5); + GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(TEXTBOX, COLOR_SELECTED_FG, 0xf0fffeff); GuiSetStyle(TEXTBOX, COLOR_SELECTED_BG, 0x839affe0); - GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); - GuiSetStyle(SPINNER, SELECT_BUTTON_WIDTH, 20); - GuiSetStyle(SPINNER, SELECT_BUTTON_PADDING, 2); - GuiSetStyle(SPINNER, SELECT_BUTTON_BORDER_WIDTH, 1); + GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 20); + GuiSetStyle(SPINNER, SPIN_BUTTON_PADDING, 2); GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0); - GuiSetStyle(SCROLLBAR, INNER_PADDING, 0); GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); - GuiSetStyle(SCROLLBAR, SLIDER_PADDING, 0); - GuiSetStyle(SCROLLBAR, SLIDER_SIZE, 16); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0); + GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); + GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 10); - GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, 0x1e); - GuiSetStyle(LISTVIEW, ELEMENTS_PADDING, 2); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 0x1e); + GuiSetStyle(LISTVIEW, LIST_ITEMS_PADDING, 2); GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 10); GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 6); - GuiSetStyle(COLORPICKER, BAR_WIDTH, 0x14); - GuiSetStyle(COLORPICKER, BAR_PADDING, 0xa); - GuiSetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT, 6); - GuiSetStyle(COLORPICKER, BAR_SELECTOR_PADDING, 2); -} + GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 0x14); + GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 0xa); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 6); + GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); -// Updates controls style with default values -RAYGUIDEF void GuiUpdateStyleComplete(void) -{ - // Populate all controls with default style - // NOTE: Extended style properties are ignored - for (int i = 1; i < NUM_CONTROLS; i++) - { - for (int j = 0; j < NUM_PROPS_DEFAULT; j++) GuiSetStyle(i, j, GuiGetStyle(DEFAULT, j)); - } + guiFont = GetFontDefault(); // Initialize default font } // 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 -RAYGUIDEF const char *GuiIconText(int iconId, const char *text) +const char *GuiIconText(int iconId, const char *text) { +#if defined(RAYGUI_SUPPORT_ICONS) static char buffer[1024] = { 0 }; memset(buffer, 0, 1024); @@ -4278,11 +3033,342 @@ RAYGUIDEF const char *GuiIconText(int iconId, const char *text) } return buffer; +#else + return NULL; +#endif } +#if defined(RAYGUI_SUPPORT_ICONS) + +// Get full icons data pointer +unsigned int *GuiGetIcons(void) { return guiIcons; } + +// Load raygui icons file (.rgi) +// NOTE: In case nameIds are required, they can be requested with loadIconsName, +// they are returned as a guiIconsName[iconsCount][RICON_MAX_NAME_LENGTH], +// 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] = ""; + short version = 0; + short reserved = 0; + short iconsCount = 0; + short iconsSize = 0; + + fread(signature, 1, 4, rgiFile); + fread(&version, 1, sizeof(short), rgiFile); + fread(&reserved, 1, sizeof(short), rgiFile); + fread(&iconsCount, 1, sizeof(short), rgiFile); + fread(&iconsSize, 1, sizeof(short), rgiFile); + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'I') && + (signature[3] == ' ')) + { + if (loadIconsName) + { + guiIconsName = (char **)RAYGUI_MALLOC(iconsCount*sizeof(char **)); + for (int i = 0; i < iconsCount; i++) + { + guiIconsName[i] = (char *)RAYGUI_MALLOC(RICON_MAX_NAME_LENGTH); + fread(guiIconsName[i], 32, 1, rgiFile); + } + } + + // Read icons data directly over guiIcons data array + fread(guiIcons, iconsCount*(iconsSize*iconsSize/32), sizeof(unsigned int), rgiFile); + } + + fclose(rgiFile); + } + + return guiIconsName; +} + +// Draw selected icon using rectangles pixel-by-pixel +void GuiDrawIcon(int iconId, Vector2 position, int pixelSize, Color color) +{ + #define BIT_CHECK(a,b) ((a) & (1<<(b))) + + for (int i = 0, y = 0; i < RICON_SIZE*RICON_SIZE/32; i++) + { + for (int k = 0; k < 32; k++) + { + if (BIT_CHECK(guiIcons[iconId*RICON_DATA_ELEMENTS + i], k)) + { + #if !defined(RAYGUI_STANDALONE) + DrawRectangle(position.x + (k%RICON_SIZE)*pixelSize, position.y + y*pixelSize, pixelSize, pixelSize, color); + #endif + } + + if ((k == 15) || (k == 31)) y++; + } + } +} + +// Get icon bit data +// NOTE: Bit data array grouped as unsigned int (ICON_SIZE*ICON_SIZE/32 elements) +unsigned int *GuiGetIconData(int iconId) +{ + static unsigned int iconData[RICON_DATA_ELEMENTS] = { 0 }; + memset(iconData, 0, RICON_DATA_ELEMENTS*sizeof(unsigned int)); + + if (iconId < RICON_MAX_ICONS) memcpy(iconData, &guiIcons[iconId*RICON_DATA_ELEMENTS], RICON_DATA_ELEMENTS*sizeof(unsigned int)); + + return iconData; +} + +// Set icon bit data +// NOTE: Data must be provided as unsigned int array (ICON_SIZE*ICON_SIZE/32 elements) +void GuiSetIconData(int iconId, unsigned int *data) +{ + if (iconId < RICON_MAX_ICONS) memcpy(&guiIcons[iconId*RICON_DATA_ELEMENTS], data, RICON_DATA_ELEMENTS*sizeof(unsigned int)); +} + +// Set icon pixel value +void GuiSetIconPixel(int iconId, int x, int y) +{ + #define BIT_SET(a,b) ((a) |= (1<<(b))) + + // This logic works for any RICON_SIZE pixels icons, + // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int data element + BIT_SET(guiIcons[iconId*RICON_DATA_ELEMENTS + y/(sizeof(unsigned int)*8/RICON_SIZE)], x + (y%(sizeof(unsigned int)*8/RICON_SIZE)*RICON_SIZE)); +} + +// Clear icon pixel value +void GuiClearIconPixel(int iconId, int x, int y) +{ + #define BIT_CLEAR(a,b) ((a) &= ~((1)<<(b))) + + // This logic works for any RICON_SIZE pixels icons, + // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int data element + BIT_CLEAR(guiIcons[iconId*RICON_DATA_ELEMENTS + y/(sizeof(unsigned int)*8/RICON_SIZE)], x + (y%(sizeof(unsigned int)*8/RICON_SIZE)*RICON_SIZE)); +} + +// Check icon pixel value +bool GuiCheckIconPixel(int iconId, int x, int y) +{ + #define BIT_CHECK(a,b) ((a) & (1<<(b))) + + return (BIT_CHECK(guiIcons[iconId*8 + y/2], x + (y%2*16))); +} +#endif // RAYGUI_SUPPORT_ICONS + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- +// Gui get text width using default font +static int GetTextWidth(const char *text) +{ + Vector2 size = { 0 }; + + if ((text != NULL) && (text[0] != '\0')) size = MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)); + + // TODO: Consider text icon width here??? + + return (int)size.x; +} + +// 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); + textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH); + + // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT + switch (control) + { + case COMBOBOX: bounds.width -= (GuiGetStyle(control, COMBO_BUTTON_WIDTH) + GuiGetStyle(control, COMBO_BUTTON_PADDING)); break; + case VALUEBOX: break; // NOTE: ValueBox text value always centered, text padding applies to label + default: + { + if (GuiGetStyle(control, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); + else textBounds.x += GuiGetStyle(control, TEXT_PADDING); + } break; + } + + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) + // More special cases (label side): CHECKBOX, SLIDER, VALUEBOX, SPINNER + + 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_SUPPORT_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! + if (*iconId >= 0) text += (pos + 1); + } + } +#endif + + return text; +} + +// Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) +{ + #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect + + if ((text != NULL) && (text[0] != '\0')) + { + int iconId = 0; + text = GetTextIcon(text, &iconId); // Check text for icon and move cursor + + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + #define ICON_TEXT_PADDING 4 + + Vector2 position = { bounds.x, bounds.y }; + + // NOTE: We get text size after icon been processed + int textWidth = GetTextWidth(text); + int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); + +#if defined(RAYGUI_SUPPORT_ICONS) + if (iconId >= 0) + { + textWidth += RICON_SIZE; + + // WARNING: If only icon provided, text could be pointing to eof character! + if ((text != NULL) && (text[0] != '\0')) textWidth += ICON_TEXT_PADDING; + } +#endif + // Check guiTextAlign global variables + switch (alignment) + { + case GUI_TEXT_ALIGN_LEFT: + { + position.x = bounds.x; + position.y = bounds.y + bounds.height/2 - textHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); + } break; + case GUI_TEXT_ALIGN_CENTER: + { + position.x = bounds.x + bounds.width/2 - textWidth/2; + position.y = bounds.y + bounds.height/2 - textHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); + } break; + case GUI_TEXT_ALIGN_RIGHT: + { + position.x = bounds.x + bounds.width - textWidth; + position.y = bounds.y + bounds.height/2 - textHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); + } break; + default: break; + } + + // NOTE: Make sure we get pixel-perfect coordinates, + // In case of decimals we got weird text positioning + position.x = (float)((int)position.x); + position.y = (float)((int)position.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if defined(RAYGUI_SUPPORT_ICONS) + if (iconId >= 0) + { + // NOTE: We consider icon height, probably different than text size + GuiDrawIcon(iconId, RAYGUI_CLITERAL(Vector2){ position.x, bounds.y + bounds.height/2 - RICON_SIZE/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height) }, 1, tint); + position.x += (RICON_SIZE + ICON_TEXT_PADDING); + } +#endif + DrawTextEx(guiFont, text, position, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + //--------------------------------------------------------------------------------- + } +} + +// 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(rec.x, rec.y, rec.width, rec.height, color); + } + + if (borderWidth > 0) + { + // Draw rectangle border lines with color + DrawRectangle(rec.x, rec.y, rec.width, borderWidth, borderColor); + DrawRectangle(rec.x, rec.y + borderWidth, borderWidth, rec.height - 2*borderWidth, borderColor); + DrawRectangle(rec.x + rec.width - borderWidth, rec.y + borderWidth, borderWidth, rec.height - 2*borderWidth, borderColor); + DrawRectangle(rec.x, rec.y + rec.height - borderWidth, rec.width, borderWidth, borderColor); + } + + // TODO: For n-patch-based style we would need: [state] and maybe [control] + // In this case all controls drawing logic should be moved to this function... I don't like it... +} + +// Draw tooltip relatively to bounds +static void GuiDrawTooltip(Rectangle bounds) +{ + //static int tooltipFramesCounter = 0; // Not possible gets reseted at second function call! + + if (guiTooltipEnabled && (guiTooltip != NULL) && CheckCollisionPointRec(GetMousePosition(), bounds)) + { + Vector2 mousePosition = GetMousePosition(); + Vector2 textSize = MeasureTextEx(guiFont, guiTooltip, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)); + Rectangle tooltipBounds = { mousePosition.x, mousePosition.y, textSize.x + 20, textSize.y*2 }; + + GuiDrawRectangle(tooltipBounds, 1, Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), guiAlpha), Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); + + tooltipBounds.x += 10; + GuiLabel(tooltipBounds, guiTooltip); + } +} // Split controls text into multiple strings // Also check for multiple columns (required by GuiToggleGroup()) @@ -4291,15 +3377,21 @@ static const char **GuiTextSplit(const char *text, 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 MAX_SUBSTRINGS_COUNT - // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH + // 1. Maximum number of possible split strings is set by TEXTSPLIT_MAX_TEXT_ELEMENTS + // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_LENGTH + // NOTE: Those definitions could be externally provided if required - #define MAX_TEXT_BUFFER_LENGTH 1024 - #define MAX_SUBSTRINGS_COUNT 64 + #if !defined(TEXTSPLIT_MAX_TEXT_LENGTH) + #define TEXTSPLIT_MAX_TEXT_LENGTH 1024 + #endif - static const char *result[MAX_SUBSTRINGS_COUNT] = { NULL }; - static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; - memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); + #if !defined(TEXTSPLIT_MAX_TEXT_ELEMENTS) + #define TEXTSPLIT_MAX_TEXT_ELEMENTS 128 + #endif + + static const char *result[TEXTSPLIT_MAX_TEXT_ELEMENTS] = { NULL }; + static char buffer[TEXTSPLIT_MAX_TEXT_LENGTH] = { 0 }; + memset(buffer, 0, TEXTSPLIT_MAX_TEXT_LENGTH); result[0] = buffer; int counter = 1; @@ -4307,7 +3399,7 @@ static const char **GuiTextSplit(const char *text, int *count, int *textRow) if (textRow != NULL) textRow[0] = 0; // Count how many substrings we have on text and point to every one - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + for (int i = 0; i < TEXTSPLIT_MAX_TEXT_LENGTH; i++) { buffer[i] = text[i]; if (buffer[i] == '\0') break; @@ -4324,7 +3416,7 @@ static const char **GuiTextSplit(const char *text, int *count, int *textRow) buffer[i] = '\0'; // Set an end of string at this point counter++; - if (counter == MAX_SUBSTRINGS_COUNT) break; + if (counter == TEXTSPLIT_MAX_TEXT_ELEMENTS) break; } } @@ -4493,8 +3585,10 @@ static Color Fade(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)(255.0f*alpha) }; - return RAYGUI_CLITERAL(Color){ color.r, color.g, color.b, (unsigned char)(255.0f*alpha) }; + return result; } // Formatting of text with variables to 'embed' @@ -4512,21 +3606,6 @@ static const char *TextFormat(const char *text, ...) return buffer; } -// Draw rectangle filled with color -static void DrawRectangleRec(Rectangle rec, Color color) -{ - DrawRectangle(rec.x, rec.y, rec.width, rec.height, color); -} - -// Draw rectangle border lines with color -static void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) -{ - DrawRectangle(rec.x, rec.y, rec.width, lineThick, color); - DrawRectangle(rec.x, rec.y + lineThick, lineThick, rec.height - 2*lineThick, color); - DrawRectangle(rec.x + rec.width - lineThick, rec.y + lineThick, lineThick, rec.height - 2*lineThick, color); - DrawRectangle(rec.x, rec.y + rec.height - lineThick, rec.width, lineThick, color); -} - // 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) @@ -4535,6 +3614,105 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co DrawRectangleGradientEx(bounds, color1, color2, color2, color1); } +#define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH 1024 // Size of static buffer: TextSplit() +#define TEXTSPLIT_MAX_SUBSTRINGS_COUNT 128 // Size of static pointers array: TextSplit() + + +// 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 TEXTSPLIT_MAX_SUBSTRINGS_COUNT + // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH + + static const char *result[TEXTSPLIT_MAX_SUBSTRINGS_COUNT] = { NULL }; + static char buffer[TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(buffer, 0, TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH); + + 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 < TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH; 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 == TEXTSPLIT_MAX_SUBSTRINGS_COUNT) 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; +} + +// Encode codepoint into utf8 text (char array length returned as parameter) +static const char *CodepointToUtf8(int codepoint, int *byteLength) +{ + static char utf8[6] = { 0 }; + int length = 0; + + if (codepoint <= 0x7f) + { + utf8[0] = (char)codepoint; + length = 1; + } + else if (codepoint <= 0x7ff) + { + utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); + utf8[1] = (char)((codepoint & 0x3f) | 0x80); + length = 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); + length = 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); + length = 4; + } + + *byteLength = length; + + return utf8; +} #endif // RAYGUI_STANDALONE -#endif // RAYGUI_IMPLEMENTATION \ No newline at end of file +#endif // RAYGUI_IMPLEMENTATION diff --git a/examples/shapes/ricons.h b/examples/shapes/ricons.h new file mode 100644 index 000000000..6befa36ba --- /dev/null +++ b/examples/shapes/ricons.h @@ -0,0 +1,556 @@ +/********************************************************************************************** +* +* rIcons - Icons pack intended for tools development with raygui +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2019-2020 Ramon Santamaria (@raysan5) +* +**********************************************************************************************/ + +#ifndef RICONS_H +#define RICONS_H + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define RICON_MAX_ICONS 256 // Maximum number of icons +#define RICON_SIZE 16 // Size of icons (squared) + +#define RICON_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 +// Number of elemens depend on RICON_SIZE (by default 16x16 pixels) +#define RICON_DATA_ELEMENTS (RICON_SIZE*RICON_SIZE/32) + +//---------------------------------------------------------------------------------- +// Icons enumeration +//---------------------------------------------------------------------------------- +typedef enum { + RICON_NONE = 0, + RICON_FOLDER_FILE_OPEN = 1, + RICON_FILE_SAVE_CLASSIC = 2, + RICON_FOLDER_OPEN = 3, + RICON_FOLDER_SAVE = 4, + RICON_FILE_OPEN = 5, + RICON_FILE_SAVE = 6, + RICON_FILE_EXPORT = 7, + RICON_FILE_NEW = 8, + RICON_FILE_DELETE = 9, + RICON_FILETYPE_TEXT = 10, + RICON_FILETYPE_AUDIO = 11, + RICON_FILETYPE_IMAGE = 12, + RICON_FILETYPE_PLAY = 13, + RICON_FILETYPE_VIDEO = 14, + RICON_FILETYPE_INFO = 15, + RICON_FILE_COPY = 16, + RICON_FILE_CUT = 17, + RICON_FILE_PASTE = 18, + RICON_CURSOR_HAND = 19, + RICON_CURSOR_POINTER = 20, + RICON_CURSOR_CLASSIC = 21, + RICON_PENCIL = 22, + RICON_PENCIL_BIG = 23, + RICON_BRUSH_CLASSIC = 24, + RICON_BRUSH_PAINTER = 25, + RICON_WATER_DROP = 26, + RICON_COLOR_PICKER = 27, + RICON_RUBBER = 28, + RICON_COLOR_BUCKET = 29, + RICON_TEXT_T = 30, + RICON_TEXT_A = 31, + RICON_SCALE = 32, + RICON_RESIZE = 33, + RICON_FILTER_POINT = 34, + RICON_FILTER_BILINEAR = 35, + RICON_CROP = 36, + RICON_CROP_ALPHA = 37, + RICON_SQUARE_TOGGLE = 38, + RICON_SYMMETRY = 39, + RICON_SYMMETRY_HORIZONTAL = 40, + RICON_SYMMETRY_VERTICAL = 41, + RICON_LENS = 42, + RICON_LENS_BIG = 43, + RICON_EYE_ON = 44, + RICON_EYE_OFF = 45, + RICON_FILTER_TOP = 46, + RICON_FILTER = 47, + RICON_TARGET_POINT = 48, + RICON_TARGET_SMALL = 49, + RICON_TARGET_BIG = 50, + RICON_TARGET_MOVE = 51, + RICON_CURSOR_MOVE = 52, + RICON_CURSOR_SCALE = 53, + RICON_CURSOR_SCALE_RIGHT = 54, + RICON_CURSOR_SCALE_LEFT = 55, + RICON_UNDO = 56, + RICON_REDO = 57, + RICON_REREDO = 58, + RICON_MUTATE = 59, + RICON_ROTATE = 60, + RICON_REPEAT = 61, + RICON_SHUFFLE = 62, + RICON_EMPTYBOX = 63, + RICON_TARGET = 64, + RICON_TARGET_SMALL_FILL = 65, + RICON_TARGET_BIG_FILL = 66, + RICON_TARGET_MOVE_FILL = 67, + RICON_CURSOR_MOVE_FILL = 68, + RICON_CURSOR_SCALE_FILL = 69, + RICON_CURSOR_SCALE_RIGHT_FILL = 70, + RICON_CURSOR_SCALE_LEFT_FILL = 71, + RICON_UNDO_FILL = 72, + RICON_REDO_FILL = 73, + RICON_REREDO_FILL = 74, + RICON_MUTATE_FILL = 75, + RICON_ROTATE_FILL = 76, + RICON_REPEAT_FILL = 77, + RICON_SHUFFLE_FILL = 78, + RICON_EMPTYBOX_SMALL = 79, + RICON_BOX = 80, + RICON_BOX_TOP = 81, + RICON_BOX_TOP_RIGHT = 82, + RICON_BOX_RIGHT = 83, + RICON_BOX_BOTTOM_RIGHT = 84, + RICON_BOX_BOTTOM = 85, + RICON_BOX_BOTTOM_LEFT = 86, + RICON_BOX_LEFT = 87, + RICON_BOX_TOP_LEFT = 88, + RICON_BOX_CENTER = 89, + RICON_BOX_CIRCLE_MASK = 90, + RICON_POT = 91, + RICON_ALPHA_MULTIPLY = 92, + RICON_ALPHA_CLEAR = 93, + RICON_DITHERING = 94, + RICON_MIPMAPS = 95, + RICON_BOX_GRID = 96, + RICON_GRID = 97, + RICON_BOX_CORNERS_SMALL = 98, + RICON_BOX_CORNERS_BIG = 99, + RICON_FOUR_BOXES = 100, + RICON_GRID_FILL = 101, + RICON_BOX_MULTISIZE = 102, + RICON_ZOOM_SMALL = 103, + RICON_ZOOM_MEDIUM = 104, + RICON_ZOOM_BIG = 105, + RICON_ZOOM_ALL = 106, + RICON_ZOOM_CENTER = 107, + RICON_BOX_DOTS_SMALL = 108, + RICON_BOX_DOTS_BIG = 109, + RICON_BOX_CONCENTRIC = 110, + RICON_BOX_GRID_BIG = 111, + RICON_OK_TICK = 112, + RICON_CROSS = 113, + RICON_ARROW_LEFT = 114, + RICON_ARROW_RIGHT = 115, + RICON_ARROW_BOTTOM = 116, + RICON_ARROW_TOP = 117, + RICON_ARROW_LEFT_FILL = 118, + RICON_ARROW_RIGHT_FILL = 119, + RICON_ARROW_BOTTOM_FILL = 120, + RICON_ARROW_TOP_FILL = 121, + RICON_AUDIO = 122, + RICON_FX = 123, + RICON_WAVE = 124, + RICON_WAVE_SINUS = 125, + RICON_WAVE_SQUARE = 126, + RICON_WAVE_TRIANGULAR = 127, + RICON_CROSS_SMALL = 128, + RICON_PLAYER_PREVIOUS = 129, + RICON_PLAYER_PLAY_BACK = 130, + RICON_PLAYER_PLAY = 131, + RICON_PLAYER_PAUSE = 132, + RICON_PLAYER_STOP = 133, + RICON_PLAYER_NEXT = 134, + RICON_PLAYER_RECORD = 135, + RICON_MAGNET = 136, + RICON_LOCK_CLOSE = 137, + RICON_LOCK_OPEN = 138, + RICON_CLOCK = 139, + RICON_TOOLS = 140, + RICON_GEAR = 141, + RICON_GEAR_BIG = 142, + RICON_BIN = 143, + RICON_HAND_POINTER = 144, + RICON_LASER = 145, + RICON_COIN = 146, + RICON_EXPLOSION = 147, + RICON_1UP = 148, + RICON_PLAYER = 149, + RICON_PLAYER_JUMP = 150, + RICON_KEY = 151, + RICON_DEMON = 152, + RICON_TEXT_POPUP = 153, + RICON_GEAR_EX = 154, + RICON_CRACK = 155, + RICON_CRACK_POINTS = 156, + RICON_STAR = 157, + RICON_DOOR = 158, + RICON_EXIT = 159, + RICON_MODE_2D = 160, + RICON_MODE_3D = 161, + RICON_CUBE = 162, + RICON_CUBE_FACE_TOP = 163, + RICON_CUBE_FACE_LEFT = 164, + RICON_CUBE_FACE_FRONT = 165, + RICON_CUBE_FACE_BOTTOM = 166, + RICON_CUBE_FACE_RIGHT = 167, + RICON_CUBE_FACE_BACK = 168, + RICON_CAMERA = 169, + RICON_SPECIAL = 170, + RICON_LINK_NET = 171, + RICON_LINK_BOXES = 172, + RICON_LINK_MULTI = 173, + RICON_LINK = 174, + RICON_LINK_BROKE = 175, + RICON_TEXT_NOTES = 176, + RICON_NOTEBOOK = 177, + RICON_SUITCASE = 178, + RICON_SUITCASE_ZIP = 179, + RICON_MAILBOX = 180, + RICON_MONITOR = 181, + RICON_PRINTER = 182, + RICON_PHOTO_CAMERA = 183, + RICON_PHOTO_CAMERA_FLASH = 184, + RICON_HOUSE = 185, + RICON_HEART = 186, + RICON_CORNER = 187, + RICON_VERTICAL_BARS = 188, + RICON_VERTICAL_BARS_FILL = 189, + RICON_LIFE_BARS = 190, + RICON_INFO = 191, + RICON_CROSSLINE = 192, + RICON_HELP = 193, + RICON_FILETYPE_ALPHA = 194, + RICON_FILETYPE_HOME = 195, + RICON_LAYERS_VISIBLE = 196, + RICON_LAYERS = 197, + RICON_WINDOW = 198, + RICON_HIDPI = 199, + RICON_200 = 200, + RICON_201 = 201, + RICON_202 = 202, + RICON_203 = 203, + RICON_204 = 204, + RICON_205 = 205, + RICON_206 = 206, + RICON_207 = 207, + RICON_208 = 208, + RICON_209 = 209, + RICON_210 = 210, + RICON_211 = 211, + RICON_212 = 212, + RICON_213 = 213, + RICON_214 = 214, + RICON_215 = 215, + RICON_216 = 216, + RICON_217 = 217, + RICON_218 = 218, + RICON_219 = 219, + RICON_220 = 220, + RICON_221 = 221, + RICON_222 = 222, + RICON_223 = 223, + RICON_224 = 224, + RICON_225 = 225, + RICON_226 = 226, + RICON_227 = 227, + RICON_228 = 228, + RICON_229 = 229, + RICON_230 = 230, + RICON_231 = 231, + RICON_232 = 232, + RICON_233 = 233, + RICON_234 = 234, + RICON_235 = 235, + RICON_236 = 236, + RICON_237 = 237, + RICON_238 = 238, + RICON_239 = 239, + RICON_240 = 240, + RICON_241 = 241, + RICON_242 = 242, + RICON_243 = 243, + RICON_244 = 244, + RICON_245 = 245, + RICON_246 = 246, + RICON_247 = 247, + RICON_248 = 248, + RICON_249 = 249, + RICON_250 = 250, + RICON_251 = 251, + RICON_252 = 252, + RICON_253 = 253, + RICON_254 = 254, + RICON_255 = 255, +} guiIconName; + +#endif // RICONS_H + +#if defined(RICONS_IMPLEMENTATION) +//---------------------------------------------------------------------------------- +// Icons data (allocated on memory data section by default) +// NOTE: A new icon set could be loaded over this array using GuiLoadIcons(), +// just note that loaded icons set must be same RICON_SIZE +//---------------------------------------------------------------------------------- +static unsigned int guiIcons[RICON_MAX_ICONS*RICON_DATA_ELEMENTS] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_NONE + 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // RICON_FOLDER_FILE_OPEN + 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // RICON_FILE_SAVE_CLASSIC + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // RICON_FOLDER_OPEN + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // RICON_FOLDER_SAVE + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // RICON_FILE_OPEN + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // RICON_FILE_SAVE + 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // RICON_FILE_EXPORT + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // RICON_FILE_NEW + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // RICON_FILE_DELETE + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // RICON_FILETYPE_TEXT + 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // RICON_FILETYPE_AUDIO + 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // RICON_FILETYPE_IMAGE + 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // RICON_FILETYPE_PLAY + 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // RICON_FILETYPE_VIDEO + 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // RICON_FILETYPE_INFO + 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // RICON_FILE_COPY + 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // RICON_FILE_CUT + 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // RICON_FILE_PASTE + 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_CURSOR_HAND + 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000, // RICON_CURSOR_POINTER + 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // RICON_CURSOR_CLASSIC + 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000, // RICON_PENCIL + 0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // RICON_PENCIL_BIG + 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // RICON_BRUSH_CLASSIC + 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // RICON_BRUSH_PAINTER + 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // RICON_WATER_DROP + 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // RICON_COLOR_PICKER + 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // RICON_RUBBER + 0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // RICON_COLOR_BUCKET + 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // RICON_TEXT_T + 0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // RICON_TEXT_A + 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // RICON_SCALE + 0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // RICON_RESIZE + 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // RICON_FILTER_POINT + 0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // RICON_FILTER_BILINEAR + 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // RICON_CROP + 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // RICON_CROP_ALPHA + 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002, // RICON_SQUARE_TOGGLE + 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // RICON_SIMMETRY + 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // RICON_SIMMETRY_HORIZONTAL + 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // RICON_SIMMETRY_VERTICAL + 0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // RICON_LENS + 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // RICON_LENS_BIG + 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // RICON_EYE_ON + 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // RICON_EYE_OFF + 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100, // RICON_FILTER_TOP + 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // RICON_FILTER + 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_POINT + 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_SMALL + 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_BIG + 0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // RICON_TARGET_MOVE + 0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // RICON_CURSOR_MOVE + 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // RICON_CURSOR_SCALE + 0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // RICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // RICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // RICON_UNDO + 0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // RICON_REDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // RICON_REREDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // RICON_MUTATE + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // RICON_ROTATE + 0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // RICON_REPEAT + 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // RICON_SHUFFLE + 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // RICON_EMPTYBOX + 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // RICON_TARGET + 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_SMALL_FILL + 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // RICON_TARGET_BIG_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // RICON_TARGET_MOVE_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // RICON_CURSOR_MOVE_FILL + 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // RICON_CURSOR_SCALE_FILL + 0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // RICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // RICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // RICON_UNDO_FILL + 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // RICON_REDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // RICON_REREDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // RICON_MUTATE_FILL + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // RICON_ROTATE_FILL + 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // RICON_REPEAT_FILL + 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // RICON_SHUFFLE_FILL + 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // RICON_EMPTYBOX_SMALL + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX + 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_TOP + 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_TOP_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // RICON_BOX_BOTTOM_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // RICON_BOX_BOTTOM + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // RICON_BOX_BOTTOM_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_LEFT + 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_TOP_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // RICON_BOX_CIRCLE_MASK + 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // RICON_BOX_CENTER + 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // RICON_POT + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // RICON_ALPHA_MULTIPLY + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // RICON_ALPHA_CLEAR + 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // RICON_DITHERING + 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // RICON_MIPMAPS + 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // RICON_BOX_GRID + 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // RICON_GRID + 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // RICON_BOX_CORNERS_SMALL + 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // RICON_BOX_CORNERS_BIG + 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // RICON_FOUR_BOXES + 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // RICON_GRID_FILL + 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // RICON_BOX_MULTISIZE + 0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // RICON_ZOOM_SMALL + 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // RICON_ZOOM_MEDIUM + 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // RICON_ZOOM_BIG + 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // RICON_ZOOM_ALL + 0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // RICON_ZOOM_CENTER + 0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // RICON_BOX_DOTS_SMALL + 0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000, // RICON_BOX_DOTS_BIG + 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // RICON_BOX_CONCENTRIC + 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // RICON_BOX_GRID_BIG + 0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000, // RICON_OK_TICK + 0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000, // RICON_CROSS + 0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000, // RICON_ARROW_LEFT + 0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000, // RICON_ARROW_RIGHT + 0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000, // RICON_ARROW_BOTTOM + 0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000, // RICON_ARROW_TOP + 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // RICON_ARROW_LEFT_FILL + 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // RICON_ARROW_RIGHT_FILL + 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // RICON_ARROW_BOTTOM_FILL + 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // RICON_ARROW_TOP_FILL + 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // RICON_AUDIO + 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // RICON_FX + 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // RICON_WAVE + 0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000, // RICON_WAVE_SINUS + 0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // RICON_WAVE_SQUARE + 0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000, // RICON_WAVE_TRIANGULAR + 0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000, // RICON_CROSS_SMALL + 0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // RICON_PLAYER_PREVIOUS + 0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000, // RICON_PLAYER_PLAY_BACK + 0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000, // RICON_PLAYER_PLAY + 0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // RICON_PLAYER_PAUSE + 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // RICON_PLAYER_STOP + 0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // RICON_PLAYER_NEXT + 0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // RICON_PLAYER_RECORD + 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // RICON_MAGNET + 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // RICON_LOCK_CLOSE + 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // RICON_LOCK_OPEN + 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // RICON_CLOCK + 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // RICON_TOOLS + 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // RICON_GEAR + 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // RICON_GEAR_BIG + 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // RICON_BIN + 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // RICON_HAND_POINTER + 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // RICON_LASER + 0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // RICON_COIN + 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // RICON_EXPLOSION + 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // RICON_1UP + 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // RICON_PLAYER + 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // RICON_PLAYER_JUMP + 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // RICON_KEY + 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // RICON_DEMON + 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // RICON_TEXT_POPUP + 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // RICON_GEAR_EX + 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // RICON_CRACK + 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // RICON_CRACK_POINTS + 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // RICON_STAR + 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // RICON_DOOR + 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // RICON_EXIT + 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // RICON_MODE_2D + 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // RICON_MODE_3D + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // RICON_CUBE + 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // RICON_CUBE_FACE_TOP + 0x7fe00000, 0x50386030, 0x47fe483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // RICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // RICON_CUBE_FACE_FRONT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // RICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // RICON_CUBE_FACE_RIGHT + 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // RICON_CUBE_FACE_BACK + 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // RICON_CAMERA + 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // RICON_SPECIAL + 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // RICON_LINK_NET + 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // RICON_LINK_BOXES + 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // RICON_LINK_MULTI + 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // RICON_LINK + 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // RICON_LINK_BROKE + 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // RICON_TEXT_NOTES + 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // RICON_NOTEBOOK + 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // RICON_SUITCASE + 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // RICON_SUITCASE_ZIP + 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // RICON_MAILBOX + 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // RICON_MONITOR + 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // RICON_PRINTER + 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // RICON_PHOTO_CAMERA + 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // RICON_PHOTO_CAMERA_FLASH + 0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // RICON_HOUSE + 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // RICON_HEART + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // RICON_CORNER + 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // RICON_VERTICAL_BARS + 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // RICON_VERTICAL_BARS_FILL + 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // RICON_LIFE_BARS + 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // RICON_INFO + 0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002, // RICON_CROSSLINE + 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // RICON_HELP + 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // RICON_FILETYPE_ALPHA + 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // RICON_FILETYPE_HOME + 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // RICON_LAYERS_VISIBLE + 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // RICON_LAYERS + 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // RICON_WINDOW + 0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // RICON_HIDPI + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_200 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_201 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_202 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_203 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_204 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_205 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_206 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_207 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_208 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_209 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_210 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_211 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_212 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_213 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_214 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_215 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_216 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_217 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_218 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_219 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_220 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_221 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_222 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_223 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_224 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_225 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_226 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_227 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_228 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_229 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_230 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_231 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_232 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_233 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_234 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_235 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_236 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_237 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_238 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_239 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_240 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_241 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_242 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_243 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_244 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_245 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_246 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_247 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_248 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_249 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_250 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_251 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_252 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_253 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_254 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // RICON_255 +}; +#endif // RICONS_IMPLEMENTATION diff --git a/examples/shapes/shapes_draw_rectangle_rounded.c b/examples/shapes/shapes_draw_rectangle_rounded.c index da6ec70a7..a798228fe 100644 --- a/examples/shapes/shapes_draw_rectangle_rounded.c +++ b/examples/shapes/shapes_draw_rectangle_rounded.c @@ -59,13 +59,13 @@ int main(void) if (drawRoundedRect) DrawRectangleRounded(rec, roundness, segments, Fade(MAROON, 0.2)); if (drawRoundedLines) DrawRectangleRoundedLines(rec,roundness, segments, lineThick, Fade(MAROON, 0.4)); - // Draw GUI controls - //------------------------------------------------------------------------------ - width = GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", width, 0, GetScreenWidth() - 300, true ); - height = GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", height, 0, GetScreenHeight() - 50, true); - roundness = GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", roundness, 0.0f, 1.0f, true); - lineThick = GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", lineThick, 0, 20, true); - segments = GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", segments, 0, 60, true); + // Draw GUI controls + //------------------------------------------------------------------------------ + width = GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", NULL, width, 0, GetScreenWidth() - 300); + height = GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", NULL, height, 0, GetScreenHeight() - 50); + roundness = GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", NULL, roundness, 0.0f, 1.0f); + lineThick = GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", NULL, lineThick, 0, 20); + segments = GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", NULL, segments, 0, 60); drawRoundedRect = GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", drawRoundedRect); drawRoundedLines = GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", drawRoundedLines); diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index 68075ee34..b83b3d675 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -60,7 +60,7 @@ int main(void) fontSDF.texture = LoadTextureFromImage(atlas); UnloadImage(atlas); - RL_FREE(fileData); // Free memory from loaded file + UnloadFileData(fileData); // Free memory from loaded file // Load SDF required shader (we use default vertex shader) Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/sdf.fs", GLSL_VERSION)); diff --git a/examples/textures/textures_draw_tiled.png b/examples/textures/textures_draw_tiled.png new file mode 100644 index 0000000000000000000000000000000000000000..374dc0b2e4b08667f9257a955b4f86177974b8d6 GIT binary patch literal 49779 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