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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
  • 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 literal 0 HcmV?d00001 diff --git a/examples/models/rlights.h b/examples/models/rlights.h index 6593ab4a5..19504473a 100644 --- a/examples/models/rlights.h +++ b/examples/models/rlights.h @@ -33,8 +33,6 @@ #ifndef RLIGHTS_H #define RLIGHTS_H -#include "raylib.h" - //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -67,11 +65,16 @@ typedef struct { extern "C" { // Prevents name mangling of functions #endif +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +int lightsCount = 0; // Current amount of created lights + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -void CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader); // Defines a light and get locations from PBR shader -void UpdateLightValues(Shader shader, Light light); // Send to PBR shader light values +Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader); // Defines a light and get locations from PBR shader +void UpdateLightValues(Shader shader, Light light); // Send to PBR shader light values #ifdef __cplusplus } @@ -103,8 +106,7 @@ void UpdateLightValues(Shader shader, Light light); //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static Light lights[MAX_LIGHTS] = { 0 }; -static int lightsCount = 0; // Current amount of created lights +// ... //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -116,7 +118,7 @@ static int lightsCount = 0; // Current amount of created lights //---------------------------------------------------------------------------------- // Defines a light and get locations from PBR shader -void CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader) +Light CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader) { Light light = { 0 }; @@ -146,10 +148,10 @@ void CreateLight(int type, Vector3 pos, Vector3 targ, Color color, Shader shader light.colorLoc = GetShaderLocation(shader, colorName); UpdateLightValues(shader, light); - - lights[lightsCount] = light; lightsCount++; } + + return light; } // Send to PBR shader light values diff --git a/examples/shaders/rlights.h b/examples/shaders/rlights.h index 0441062b6..f9727c9e1 100644 --- a/examples/shaders/rlights.h +++ b/examples/shaders/rlights.h @@ -68,6 +68,11 @@ typedef enum { extern "C" { // Prevents name mangling of functions #endif +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +int lightsCount = 0; // Current amount of created lights + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -104,7 +109,7 @@ void UpdateLightValues(Shader shader, Light light); // Send light proper //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static int lightsCount = 0; // Current amount of created lights +// ... //---------------------------------------------------------------------------------- // Module specific Functions Declaration diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 6d5df13eb..384b09a35 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,11 +1,11 @@ /******************************************************************************************* * -* raygui v2.5 - A simple and easy-to-use immediate-mode-gui library +* raygui v2.8 - A simple and easy-to-use immediate-mode gui library * * DESCRIPTION: * -* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also possible -* to be used as a standalone library, as long as input and drawing functions are provided. +* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also +* available as a standalone library, as long as input and drawing functions are provided. * * Controls provided: * @@ -40,10 +40,10 @@ * - Grid * * # Advance Controls -* - ListView --> 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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 z 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