From f7c3035b8c5f7b86165ba24dfa868a4527e48a2a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 24 Sep 2023 23:51:23 +0200 Subject: [PATCH] Update raygui.h --- examples/shapes/raygui.h | 1389 +++++++++++++++++++++++++++----------- 1 file changed, 985 insertions(+), 404 deletions(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 63a27fa90..26d6bacb1 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raygui v4.0-dev - A simple and easy-to-use immediate-mode gui library +* raygui v4.0 - 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 @@ -10,20 +10,26 @@ * - Immediate-mode gui, minimal retained data * - +25 controls provided (basic and advanced) * - Styling system for colors, font and metrics -* - Icons supported, embeds a complete 1-bit icons pack -* - Standalone usage mode option (custom graphics backends) -* - Multiple tools provided for raygui development +* - Icons supported, embedded as a 1-bit icons pack +* - Standalone mode option (custom input/graphics backend) +* - Multiple support tools provided for raygui development * * POSSIBLE IMPROVEMENTS: -* - Redesign functions that require a value as parameter to be returned, pass by reference * - Better standalone mode API for easy plug of custom backends -* - Externalize required inputs in some way, allow user customization +* - Externalize required inputs, allow user easier customization * * LIMITATIONS: -* - No multi-line word-wraped text box support -* - No auto-layout mechanism provided, up to the user to define controls position and size +* - No editable multi-line word-wraped text box supported +* - No auto-layout mechanism, up to the user to define controls position and size * - Standalone mode requires library modification and some user work to plug another backend * +* NOTES: +* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for +* font atlas recs and glyphs, freeing that memory is (usually) up to the user, +* no unload function is explicitly provided... but note that GuiLoadStyleDefaulf() unloads +* by default any previously loaded font (texture, recs, glyphs). +* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions +* * CONTROLS PROVIDED: * # Container/separators Controls * - WindowBox --> StatusBar, Panel @@ -31,13 +37,15 @@ * - Line * - Panel --> StatusBar * - ScrollPanel --> StatusBar +* - TabBar --> Button * * # Basic Controls * - Label -* - Button * - LabelButton --> Label +* - Button * - Toggle * - ToggleGroup --> Toggle +* - ToggleSlider * - CheckBox * - ComboBox * - DropdownBox @@ -126,11 +134,24 @@ * Includes custom ricons.h header defining a set of custom icons, * this file can be generated using rGuiIcons tool * +* #define RAYGUI_DEBUG_RECS_BOUNDS +* Draw control bounds rectangles for debug +* * #define RAYGUI_DEBUG_TEXT_BOUNDS * Draw text bounds rectangles for debug * * VERSIONS HISTORY: -* 4.0 (xx-Jun-2023) REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value +* 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() +* ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() +* ADDED: Multiple new icons, mostly compiler related +* ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE +* ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode +* ADDED: Support loading styles with custom font charset from external file +* REDESIGNED: GuiTextBox(), support mouse cursor positioning +* REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only) +* REDESIGNED: GuiProgressBar() to be more visual, progress affects border color +* REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText() +* REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value * REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value * REDESIGNED: GuiComboBox(), get parameters by reference and return result value * REDESIGNED: GuiCheckBox(), get parameters by reference and return result value @@ -146,6 +167,10 @@ * REDESIGNED: GuiGrid(), added extra parameter * REDESIGNED: GuiListViewEx(), change parameters order * REDESIGNED: All controls return result as int value +* REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars +* REVIEWED: All examples and specially controls_test_suite +* RENAMED: gui_file_dialog module to gui_window_file_dialog +* UPDATED: All styles to include ISO-8859-15 charset (as much as possible) * * 3.6 (10-May-2023) ADDED: New icon: SAND_TIMER * ADDED: GuiLoadStyleFromMemory() (binary only) @@ -221,17 +246,43 @@ * 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria. * * DEPENDENCIES: -* raylib 4.5 Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* raylib 4.6-dev Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing * +* STANDALONE MODE: * By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled * with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs. * +* The following functions should be redefined for a custom backend: +* +* - Vector2 GetMousePosition(void); +* - float GetMouseWheelMove(void); +* - bool IsMouseButtonDown(int button); +* - bool IsMouseButtonPressed(int button); +* - bool IsMouseButtonReleased(int button); +* - bool IsKeyDown(int key); +* - bool IsKeyPressed(int key); +* - int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +* +* - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +* - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +* +* - Font GetFontDefault(void); // -- GuiLoadStyleDefault() +* - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle() +* - Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +* - void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) +* - char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +* - void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data +* - const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs +* - int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +* - void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list +* - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +* * CONTRIBUTORS: * Ramon Santamaria: Supervision, review, redesign, update and maintenance * Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019) * Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018) -* Adria Arranz: Testing and Implementation of additional controls (2018) -* Jordi Jorba: Testing and Implementation of additional controls (2018) +* Adria Arranz: Testing and implementation of additional controls (2018) +* Jordi Jorba: Testing and implementation of additional controls (2018) * Albert Martos: Review and testing of the library (2015) * Ian Eito: Review and testing of the library (2015) * Kevin Gato: Initial implementation of basic components (2014) @@ -265,7 +316,7 @@ #define RAYGUI_VERSION_MAJOR 4 #define RAYGUI_VERSION_MINOR 0 #define RAYGUI_VERSION_PATCH 0 -#define RAYGUI_VERSION "4.0-dev" +#define RAYGUI_VERSION "4.0" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -390,37 +441,69 @@ } Font; #endif + // Style property +// NOTE: Used when exporting style as code for convenience typedef struct GuiStyleProp { - unsigned short controlId; - unsigned short propertyId; - unsigned int propertyValue; + unsigned short controlId; // Control identifier + unsigned short propertyId; // Property identifier + int propertyValue; // Property value } GuiStyleProp; +/* +// Controls text style -NOT USED- +// NOTE: Text style is defined by control +typedef struct GuiTextStyle { + unsigned int size; + int charSpacing; + int lineSpacing; + int alignmentH; + int alignmentV; + int padding; +} GuiTextStyle; +*/ + // Gui control state typedef enum { STATE_NORMAL = 0, STATE_FOCUSED, STATE_PRESSED, - STATE_DISABLED, + STATE_DISABLED } GuiState; // Gui control text alignment typedef enum { TEXT_ALIGN_LEFT = 0, TEXT_ALIGN_CENTER, - TEXT_ALIGN_RIGHT, + TEXT_ALIGN_RIGHT } GuiTextAlignment; +// Gui control text alignment vertical +// NOTE: Text vertical position inside the text bounds +typedef enum { + TEXT_ALIGN_TOP = 0, + TEXT_ALIGN_MIDDLE, + TEXT_ALIGN_BOTTOM +} GuiTextAlignmentVertical; + +// Gui control text wrap mode +// NOTE: Useful for multiline text +typedef enum { + TEXT_WRAP_NONE = 0, + TEXT_WRAP_CHAR, + TEXT_WRAP_WORD +} GuiTextWrapMode; + // Gui controls typedef enum { // Default -> populates to all controls when set DEFAULT = 0, + // Basic controls LABEL, // Used also for: LABELBUTTON BUTTON, TOGGLE, // Used also for: TOGGLEGROUP - SLIDER, // Used also for: SLIDERBAR + SLIDER, // Used also for: SLIDERBAR, TOGGLESLIDER PROGRESSBAR, CHECKBOX, COMBOBOX, @@ -437,37 +520,55 @@ typedef enum { // Gui base properties for every control // NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties) typedef enum { - BORDER_COLOR_NORMAL = 0, - BASE_COLOR_NORMAL, - TEXT_COLOR_NORMAL, - BORDER_COLOR_FOCUSED, - BASE_COLOR_FOCUSED, - TEXT_COLOR_FOCUSED, - BORDER_COLOR_PRESSED, - BASE_COLOR_PRESSED, - TEXT_COLOR_PRESSED, - BORDER_COLOR_DISABLED, - BASE_COLOR_DISABLED, - TEXT_COLOR_DISABLED, - BORDER_WIDTH, - TEXT_PADDING, - TEXT_ALIGNMENT, - RESERVED + BORDER_COLOR_NORMAL = 0, // Control border color in STATE_NORMAL + BASE_COLOR_NORMAL, // Control base color in STATE_NORMAL + TEXT_COLOR_NORMAL, // Control text color in STATE_NORMAL + BORDER_COLOR_FOCUSED, // Control border color in STATE_FOCUSED + BASE_COLOR_FOCUSED, // Control base color in STATE_FOCUSED + TEXT_COLOR_FOCUSED, // Control text color in STATE_FOCUSED + BORDER_COLOR_PRESSED, // Control border color in STATE_PRESSED + BASE_COLOR_PRESSED, // Control base color in STATE_PRESSED + TEXT_COLOR_PRESSED, // Control text color in STATE_PRESSED + BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED + BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED + TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED + BORDER_WIDTH, // Control border size, 0 for no border + //TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls + //TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls + //TEXT_LINE_SPACING // Control text spacing between lines -> GLOBAL for all controls + TEXT_PADDING, // Control text padding, not considering border + TEXT_ALIGNMENT, // Control text horizontal alignment inside control text bound (after border and padding) + //TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls } GuiControlProperty; -// Gui extended properties depend on control -// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default 8 properties) -//---------------------------------------------------------------------------------- +// TODO: Which text styling properties should be global or per-control? +// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while +// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and +// should be configured by user as needed while defining the UI layout + +// Gui extended properties depend on control +// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties) +//---------------------------------------------------------------------------------- // DEFAULT extended properties // NOTE: Those properties are common to all controls or global +// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT? typedef enum { TEXT_SIZE = 16, // Text size (glyphs max height) TEXT_SPACING, // Text spacing between glyphs LINE_COLOR, // Line control color BACKGROUND_COLOR, // Background color + TEXT_LINE_SPACING, // Text spacing between lines + TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding) + TEXT_WRAP_MODE // Text wrap-mode inside text bounds + //TEXT_DECORATION // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline + //TEXT_DECORATION_THICK // Text decoration line thikness } GuiDefaultProperty; +// Other possible text properties: +// TEXT_WEIGHT // Normal, Italic, Bold -> Requires specific font change +// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING... + // Label //typedef enum { } GuiLabelProperty; @@ -492,12 +593,12 @@ typedef enum { // ScrollBar typedef enum { - ARROWS_SIZE = 16, - ARROWS_VISIBLE, - SCROLL_SLIDER_PADDING, // (SLIDERBAR, SLIDER_PADDING) - SCROLL_SLIDER_SIZE, - SCROLL_PADDING, - SCROLL_SPEED, + ARROWS_SIZE = 16, // ScrollBar arrows size + ARROWS_VISIBLE, // ScrollBar arrows visible + SCROLL_SLIDER_PADDING, // ScrollBar slider internal padding + SCROLL_SLIDER_SIZE, // ScrollBar slider size + SCROLL_PADDING, // ScrollBar scroll padding from arrows + SCROLL_SPEED, // ScrollBar scrolling speed } GuiScrollBarProperty; // CheckBox @@ -519,11 +620,7 @@ typedef enum { // TextBox/TextBoxMulti/ValueBox/Spinner typedef enum { - TEXT_INNER_PADDING = 16, // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding - TEXT_LINES_SPACING, // TextBoxMulti lines separation - TEXT_ALIGNMENT_VERTICAL, // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN - TEXT_MULTILINE, // TextBox supports multiple lines - TEXT_WRAP_MODE // TextBox wrap mode for multiline: 0-NO_WRAP, 1-CHAR_WRAP, 2-WORD_WRAP + TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable } GuiTextBoxProperty; // Spinner @@ -537,7 +634,7 @@ typedef enum { LIST_ITEMS_HEIGHT = 16, // ListView items height LIST_ITEMS_SPACING, // ListView items separation SCROLLBAR_WIDTH, // ListView scrollbar size (usually width) - SCROLLBAR_SIDE, // ListView scrollbar side (0-left, 1-right) + SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE) } GuiListViewProperty; // ColorPicker @@ -571,7 +668,7 @@ RAYGUIAPI void GuiDisable(void); // Disable gui c RAYGUIAPI void GuiLock(void); // Lock gui controls (global state) RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state) RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state) -RAYGUIAPI void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +RAYGUIAPI void GuiSetAlpha(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f RAYGUIAPI void GuiSetState(int state); // Set gui state (global state) RAYGUIAPI int GuiGetState(void); // Get gui state (global state) @@ -618,6 +715,7 @@ RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control, returns true when active RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control, returns active toggle index +RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control, returns true when clicked RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control, returns selected item index @@ -642,6 +740,8 @@ RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color); // Color Panel control RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control +RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Picker control that avoids conversion to RGB on each call (multiple color controls) +RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that returns HSV color value, used by GuiColorPickerHSV() //---------------------------------------------------------------------------------------------------------- @@ -1134,11 +1234,11 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP - 0x7fe00000, 0x50386030, 0x47fe483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT - 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT - 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK + 0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET @@ -1237,8 +1337,10 @@ static unsigned int *guiIconsPtr = guiIcons; #define RAYGUI_ICON_SIZE 0 #endif -#define RAYGUI_MAX_CONTROLS 16 // Maximum number of standard controls -#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of standard properties +// WARNING: Those values define the total size of the style data array, +// if changed, previous saved styles could become incompatible +#define RAYGUI_MAX_CONTROLS 16 // Maximum number of controls +#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of base properties #define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties //---------------------------------------------------------------------------------- @@ -1254,7 +1356,7 @@ static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_N 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 +static float guiAlpha = 1.0f; // Gui controls transparency static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) @@ -1264,7 +1366,7 @@ static const char *guiTooltipPtr = NULL; // Tooltip string pointer (strin static bool guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider) static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier -static unsigned int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() +static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() //static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement @@ -1317,25 +1419,33 @@ static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle(), GuiDrawIcon() +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() //------------------------------------------------------------------------------- // Text required functions //------------------------------------------------------------------------------- -static Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // -- GuiLoadStyle() -static Font GetFontDefault(void); // -- GuiLoadStyleDefault() -static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle() -static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle() -static char *LoadFileText(const char *fileName); // -- GuiLoadStyle() -static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle() +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font + +static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) + +static char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +static void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data + +static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs + +static int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +static void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list + +static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() //------------------------------------------------------------------------------- // raylib functions already implemented in raygui //------------------------------------------------------------------------------- static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value static int ColorToInt(Color color); // Returns hexadecimal value for a Color -static 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 @@ -1358,7 +1468,7 @@ static int GetTextWidth(const char *text); // Gui get text static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor -static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint); // Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings @@ -1368,6 +1478,7 @@ static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll bar control, used by GuiScrollPanel() static void GuiTooltip(Rectangle controlRec); // Draw tooltip using control rec position +static Color GuiFade(Color color, float alpha); // Fade color by an alpha factor //---------------------------------------------------------------------------------- // Gui Setup Functions Definition @@ -1390,7 +1501,7 @@ void GuiUnlock(void) { guiLocked = false; } bool GuiIsLocked(void) { return guiLocked; } // Set gui controls alpha global state -void GuiFade(float alpha) +void GuiSetAlpha(float alpha) { if (alpha < 0.0f) alpha = 0.0f; else if (alpha > 1.0f) alpha = 1.0f; @@ -1416,7 +1527,6 @@ void GuiSetFont(Font font) if (!guiStyleLoaded) GuiLoadStyleDefault(); guiFont = font; - GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); } } @@ -1487,7 +1597,7 @@ int GuiWindowBox(Rectangle bounds, const char *title) GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); #if defined(RAYGUI_NO_ICONS) - clicked = GuiButton(closeButtonRec, "x"); + result = GuiButton(closeButtonRec, "x"); #else result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); #endif @@ -1510,9 +1620,9 @@ int GuiGroupBox(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR))); GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text); //-------------------------------------------------------------------- @@ -1533,7 +1643,7 @@ int GuiLine(Rectangle bounds, const char *text) int result = 0; GuiState state = guiState; - Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha); + Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)); // Draw control //-------------------------------------------------------------------- @@ -1574,15 +1684,15 @@ int GuiPanel(Rectangle bounds, const char *text) { // Move panel bounds after the header bar bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; - bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; } // Draw control //-------------------------------------------------------------------- if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar - GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha), - Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); + GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), + GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR))); //-------------------------------------------------------------------- return result; @@ -1595,7 +1705,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) #define RAYGUI_TABBAR_ITEM_WIDTH 160 int result = -1; - GuiState state = guiState; + //GuiState state = guiState; Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height }; @@ -1638,7 +1748,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); // Draw tab close button - // NOTE: Only draw close button for curren tab: if (CheckCollisionPointRec(mousePoint, tabBounds)) + // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds)) int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, BORDER_WIDTH, 1); @@ -1663,8 +1773,12 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) // Scroll Panel control int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view) { + #define RAYGUI_MIN_SCROLLBAR_WIDTH 40 + #define RAYGUI_MIN_SCROLLBAR_HEIGHT 40 + int result = 0; GuiState state = guiState; + float mouseWheelSpeed = 20.0f; // Default movement speed with mouse wheel Rectangle temp = { 0 }; if (view == NULL) view = &temp; @@ -1692,8 +1806,31 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; - Rectangle horizontalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)horizontalScrollBarWidth }; - Rectangle verticalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)verticalScrollBarWidth, (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; + Rectangle horizontalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)horizontalScrollBarWidth + }; + Rectangle verticalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), + (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)verticalScrollBarWidth, + (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Make sure scroll bars have a minimum width/height + // NOTE: If content >>> bounds, size could be very small or even 0 + if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) + { + horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; + mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.height vs bounds.height + } + if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) + { + verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; + mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.width vs bounds.width + } // Calculate view area (area without the scrollbars) *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? @@ -1736,9 +1873,9 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector #endif float wheelMove = GetMouseWheelMove(); - // Horizontal scroll ((Left Control or Left Shift) + Mouse wheel) - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*20; - else scrollPos.y += wheelMove*20; // Vertical scroll + // Horizontal and vertical scrolling with mouse wheel + if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed; + else scrollPos.y += wheelMove*mouseWheelSpeed; // Vertical scroll } } @@ -1753,7 +1890,7 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector //-------------------------------------------------------------------- if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar - GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); // Draw background + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background // Save size of the scrollbar slider const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); @@ -1780,11 +1917,11 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector if (hasHorizontalScrollBar && hasVerticalScrollBar) { Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 }; - GuiDrawRectangle(corner, 0, BLANK, Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3)))); } // Draw scrollbar lines depending on current state - GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK); // Set scrollbar slider size back to the way it was before GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); @@ -1808,7 +1945,7 @@ int GuiLabel(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -1839,8 +1976,8 @@ int GuiButton(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - 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)); + GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3)))); if (state == STATE_FOCUSED) GuiTooltip(bounds); //------------------------------------------------------------------ @@ -1877,7 +2014,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return pressed; @@ -1916,13 +2053,13 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) //-------------------------------------------------------------------- if (state == STATE_NORMAL) { - 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)); + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3))))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3))))); } else { - GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), 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)); + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3))); } if (state == STATE_FOCUSED) GuiTooltip(bounds); @@ -1980,7 +2117,79 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) return result; } -// Check Box control, returns true when active +// Toggle Slider control extended, returns true when clicked +int GuiToggleSlider(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + //bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle slider = { + 0, // Calculated later depending on the active toggle + bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount, + bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + (*active)++; + result = 1; + } + else state = STATE_FOCUSED; + } + + if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED; + } + + if (*active >= itemCount) *active = 0; + slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))), + GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL))); + + // Draw internal slider + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + + // Draw text in slider + if (text != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = slider.x + slider.width/2 - textBounds.width/2; + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return result; +} + +// Check Box control, returns 1 when state changed int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) { int result = 0; @@ -2019,14 +2228,18 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) *checked = !(*checked); + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + *checked = !(*checked); + result = 1; + } } } //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK); if (*checked) { @@ -2034,10 +2247,10 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) 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)); + GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3))); } - GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2088,8 +2301,8 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Draw control //-------------------------------------------------------------------- // Draw combo box main - 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)); + GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3)))); + GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3)))); // Draw selector using a custom button // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values @@ -2184,8 +2397,8 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod //-------------------------------------------------------------------- if (editMode) GuiPanel(boundsOpen, NULL); - 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)); + GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3))); + GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3))); if (editMode) { @@ -2197,25 +2410,25 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod 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)); + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED))); } else if (i == itemFocused) { - GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), 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)); + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED))); } - else GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)), guiAlpha)); + else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL))); } } // Draw arrows (using icon if available) #if defined(RAYGUI_NO_ICONS) GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); #else GuiDrawText("#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); // ICON_ARROW_DOWN_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL #endif //-------------------------------------------------------------------- @@ -2239,12 +2452,12 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) int result = 0; GuiState state = guiState; + bool multiline = false; // TODO: Consider multiline text input + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); + Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex); - int textIndexOffset = 0; // Text index offset to start drawing in the box - - int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); - int multiline = GuiGetStyle(TEXTBOX, TEXT_MULTILINE); + int textIndexOffset = 0; // Text index offset to start drawing in the box // Cursor rectangle // NOTE: Position X value should be updated @@ -2255,17 +2468,15 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2 }; - switch (alignmentVertical) - { - case 0: cursor.y = textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE); break; // CENTERED - case 1: cursor.y = textBounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; break; // UP - case 2: cursor.y = textBounds.y + textBounds.height; break; // DOWN - default: break; - } - if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); + // Mouse cursor rectangle + // NOTE: Initialized outside of screen + Rectangle mouseCursor = cursor; + mouseCursor.x = -1; + mouseCursor.width = 1; + // Auto-cursor movement logic // NOTE: Cursor moves automatically when key down after some time if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++; @@ -2281,9 +2492,14 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + // WARNING: Text editing is only supported under certain conditions: + if ((state != STATE_DISABLED) && // Control not disabled + !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode + !guiLocked && // Gui not locked + !guiSliderDragging && // No gui slider on dragging + (wrapMode == TEXT_WRAP_NONE)) // No wrap mode { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePosition = GetMousePosition(); if (editMode) { @@ -2301,7 +2517,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex); } - unsigned int textLength = (unsigned int)strlen(text); // Get current text length + int textLength = (int)strlen(text); // Get current text length int codepoint = GetCharPressed(); // Get Unicode codepoint if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; @@ -2329,16 +2545,10 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } // Move cursor to start - if ((textLength > 0) && IsKeyPressed(KEY_HOME)) - { - textBoxCursorIndex = 0; - } + if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; // Move cursor to end - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) - { - textBoxCursorIndex = textLength; - } + if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength; // Delete codepoint from text, after current cursor position if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) @@ -2359,7 +2569,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) text[textLength] = '\0'; } } - + // Delete codepoint from text, before current cursor position if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) { @@ -2386,7 +2596,6 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } // Move cursor position with keys - //if (IsKeyDown(KEY_LEFT) && autoCursorMode) if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))) { autoCursorDelayCounter++; @@ -2412,19 +2621,59 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } } - // TODO: Get cursor rectangle from mouse position - //cursor = GetCursorFromMousePosition(bounds, text, mouse); // Gui style considered internally, including wrapMode + // Move cursor position with mouse + if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text + { + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; + int codepoint = 0; + int codepointSize = 0; + int codepointIndex = 0; + float glyphWidth = 0.0f; + float widthToMouseX = 0; + int mouseCursorIndex = 0; - // TODO: Get cursor rectangle from buffer index - //cursor = GetCursorFromIndex(bounds, text, index); // Gui style considered internally, including wrapMode + for (int i = textIndexOffset; i < textLength; i++) + { + codepoint = GetCodepointNext(&text[i], &codepointSize); + codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2))) + { + mouseCursor.x = textBounds.x + widthToMouseX; + mouseCursorIndex = i; + break; + } + + widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + + // Check if mouse cursor is at the last position + int textEndWidth = GetTextWidth(text + textIndexOffset); + if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) + { + mouseCursor.x = textBounds.x + textEndWidth; + mouseCursorIndex = strlen(text); + } + + // Place cursor at required index on mouse click + if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + cursor.x = mouseCursor.x; + textBoxCursorIndex = mouseCursorIndex; + } + } + else mouseCursor.x = -1; // Recalculate cursor position.y depending on textBoxCursorIndex cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); //if (multiline) cursor.y = GetTextLines() - // Finish text editing on ENTER (if not multiline mode) or mouse click outside bounds + // Finish text editing on ENTER or mouse click outside bounds if ((!multiline && IsKeyPressed(KEY_ENTER)) || - (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) { textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index result = 1; @@ -2432,7 +2681,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } else { - if (CheckCollisionPointRec(mousePoint, bounds)) + if (CheckCollisionPointRec(mousePosition, bounds)) { state = STATE_FOCUSED; @@ -2450,23 +2699,26 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) //-------------------------------------------------------------------- if (state == STATE_PRESSED) { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); } else if (state == STATE_DISABLED) { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); } - else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); // Draw text considering index offset if required // NOTE: Text index offset depends on cursor position - GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); // Draw cursor - if (editMode) + if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) { //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) - GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + + // Draw mouse position cursor (if required) + if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); } else if (state == STATE_FOCUSED) GuiTooltip(bounds); //-------------------------------------------------------------------- @@ -2475,22 +2727,23 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } /* -// Text Box control with multiple lines -// NOTE: It's a regular GuiTextBox() but enabling multiline support, -// unfortunately cursor placement is not working properly so the function is removed +// Text Box control with multiple lines and word-wrap +// NOTE: This text-box is readonly, no editing supported by default bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode) { bool pressed = false; - GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1); - GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 1); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // WARNING: If wrap mode enabled, text editing is not supported + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); // TODO: Implement methods to calculate cursor position properly pressed = GuiTextBox(bounds, text, bufferSize, editMode); - GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0); - GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0); - + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 0); + return pressed; } */ @@ -2562,7 +2815,7 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); // Draw text label if provided - GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- *value = tempValue; @@ -2661,20 +2914,19 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); else if (state == 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), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); // Draw cursor if (editMode) { // NOTE: ValueBox internal text is always centered Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) }; - GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); } // Draw text label if provided - GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2718,7 +2970,9 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { - // Get equivalent value and slider position from mousePoint.x + state = STATE_PRESSED; + + // Get equivalent value and slider position from mousePosition.x *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; } } @@ -2736,11 +2990,14 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, guiSliderDragging = true; guiSliderActive = bounds; // Store bounds as an identifier when dragging starts - // Get equivalent value and slider position from mousePoint.x - *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + if (!CheckCollisionPointRec(mousePoint, slider)) + { + // Get equivalent value and slider position from mousePosition.x + *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; - if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider - else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider + else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + } } else state = STATE_FOCUSED; } @@ -2763,11 +3020,12 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); // Draw slider internal bar (depends on state) - if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED))); // Draw left/right text if provided if (textLeft != NULL) @@ -2778,7 +3036,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } if (textRight != NULL) @@ -2789,7 +3047,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } //-------------------------------------------------------------------- @@ -2817,6 +3075,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight float temp = (maxValue - minValue)/2.0f; if (value == NULL) value = &temp; + // Progress bar Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0, bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) }; @@ -2825,16 +3084,39 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight //-------------------------------------------------------------------- if (*value > maxValue) *value = maxValue; - if (state != STATE_DISABLED) progress.width = ((float)(*value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); + // WARNING: Working with floats could lead to rounding issues + if ((state != STATE_DISABLED)) progress.width = (float)(*value/(maxValue - minValue))*bounds.width - ((*value >= maxValue) ? (float)(2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)) : 0.0f); //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha), BLANK); + if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK); + } + else + { + if (*value > minValue) + { + // Draw progress bar with colored border, more visual + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + } + else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); - // Draw slider internal progress bar (depends on state) - if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == STATE_FOCUSED) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha)); + if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + 1, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + else + { + // Draw borders not yet reached by value + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y + bounds.height - 1, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + } + + // Draw slider internal progress bar (depends on state) + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } // Draw left/right text if provided if (textLeft != NULL) @@ -2845,7 +3127,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3)))); } if (textRight != NULL) @@ -2856,7 +3138,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3)))); } //-------------------------------------------------------------------- @@ -2871,9 +3153,8 @@ int GuiStatusBar(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha), - Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2902,8 +3183,8 @@ int GuiDummyRec(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED))); //------------------------------------------------------------------ return result; @@ -2930,7 +3211,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd GuiState state = guiState; int itemFocused = (focus == NULL)? -1 : *focus; - int itemSelected = *active; + int itemSelected = (active == NULL)? -1 : *active; // Check if we need a scroll bar bool useScrollBar = false; @@ -3002,35 +3283,35 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background // Draw visible items for (int i = 0; ((i < visibleItems) && (text != NULL)); i++) { if (state == STATE_DISABLED) { - 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)); + if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); } else { - if ((startIndex + i) == itemSelected) + if (((startIndex + i) == itemSelected) && (active != NULL)) { // 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)); + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); } - else if ((startIndex + i) == itemFocused) + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned! { // 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)); + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); } else { // Draw item normal - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); } } @@ -3062,10 +3343,10 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd } //-------------------------------------------------------------------- + if (active != NULL) *active = itemSelected; if (focus != NULL) *focus = itemFocused; if (scrollIndex != NULL) *scrollIndex = startIndex; - *active = itemSelected; return result; } @@ -3137,14 +3418,14 @@ int GuiColorPanel(Rectangle bounds, const char *text, Color *color) // Draw color picker: selector Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; - GuiDrawRectangle(selector, 0, BLANK, Fade(colWhite, guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, colWhite); } else { DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); } - GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); //-------------------------------------------------------------------- return result; @@ -3174,6 +3455,8 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { + state = STATE_PRESSED; + *alpha = (mousePoint.x - bounds.x)/bounds.width; if (*alpha <= 0.0f) *alpha = 0.0f; if (*alpha >= 1.0f) *alpha = 1.0f; @@ -3217,7 +3500,7 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) for (int y = 0; y < checksY; y++) { Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE }; - GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) : Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha)); + GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f)); } } @@ -3225,10 +3508,10 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, 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)); - GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); // Draw alpha bar: selector - GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); //-------------------------------------------------------------------- return result; @@ -3258,6 +3541,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { + state = STATE_PRESSED; + *hue = (mousePoint.y - bounds.y)*360/bounds.height; if (*hue <= 0.0f) *hue = 0.0f; if (*hue >= 359.0f) *hue = 359.0f; @@ -3303,7 +3588,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) if (state != STATE_DISABLED) { // Draw hue bar:color bars - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); + // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha)); DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha)); DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha)); @@ -3312,10 +3598,10 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); - GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); // Draw hue bar: selector - GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); //-------------------------------------------------------------------- return result; @@ -3340,9 +3626,9 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color) //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 }); - + GuiColorBarHue(boundsHue, NULL, &hsv.x); - + //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f); Vector3 rgb = ConvertHSVtoRGB(hsv); @@ -3351,6 +3637,105 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color) return result; } +// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering. +// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB. +// NOTE: It's divided in multiple controls: +// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanelHSV() size +int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + + Vector3 tempHsv = { 0 }; + + if (colorHsv == NULL) + { + const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f }; + tempHsv = ConvertRGBtoHSV(tempColor); + colorHsv = &tempHsv; + } + + GuiColorPanelHSV(bounds, NULL, colorHsv); + + const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + + GuiColorBarHue(boundsHue, NULL, &colorHsv->x); + + return result; +} + +// Color Panel control, returns HSV color value in *colorHsv. +// Used by GuiColorPickerHSV() +int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + GuiState state = guiState; + Vector2 pickerSelector = { 0 }; + + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; + + pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width; // HSV: Saturation + pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height; // HSV: Value + + float hue = -1.0f; + Vector3 maxHue = { hue >= 0.0f ? hue : colorHsv->x, 1.0f, 1.0f }; + Vector3 rgbHue = ConvertHSVtoRGB(maxHue); + Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), + (unsigned char)(255.0f*rgbHue.y), + (unsigned char)(255.0f*rgbHue.z), 255 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + pickerSelector = mousePoint; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha)); + DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0)); + + // Draw color picker: selector + Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; + GuiDrawRectangle(selector, 0, BLANK, colWhite); + } + else + { + DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); + } + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + //-------------------------------------------------------------------- + + return result; +} + // Message Box control int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons) { @@ -3371,7 +3756,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; - int textWidth = GetTextWidth(message); + int textWidth = GetTextWidth(message) + 2; Rectangle textBounds = { 0 }; textBounds.x = bounds.x + bounds.width/2 - textWidth/2; @@ -3511,8 +3896,9 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect Vector2 mousePoint = GetMousePosition(); Vector2 currentMouseCell = { 0 }; - int linesV = ((int)(bounds.width/spacing))*subdivs + 1; - int linesH = ((int)(bounds.height/spacing))*subdivs + 1; + float spaceWidth = spacing/(float)subdivs; + int linesV = (int)(bounds.width/spaceWidth) + 1; + int linesH = (int)(bounds.height/spaceWidth) + 1; // Update control //-------------------------------------------------------------------- @@ -3539,14 +3925,14 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect 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)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); + GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); } // Draw horizontal grid lines for (int i = 0; i < linesH; i++) { Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width, 1 }; - GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); + GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); } } } break; @@ -3620,34 +4006,37 @@ void GuiLoadStyle(const char *fileName) sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName); Font font = { 0 }; + int *codepoints = NULL; + int codepointCount = 0; if (charmapFileName[0] != '0') { - // Load characters from charmap file, - // expected '\n' separated list of integer values - char *charValues = LoadFileText(charmapFileName); - if (charValues != NULL) - { - int glyphCount = 0; - const char **chars = TextSplit(charValues, '\n', &glyphCount); - - int *values = (int *)RAYGUI_MALLOC(glyphCount*sizeof(int)); - for (int i = 0; i < glyphCount; i++) values[i] = TextToInteger(chars[i]); - - if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); - font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, values, glyphCount); - if (font.texture.id == 0) font = GetFontDefault(); - - RAYGUI_FREE(values); - } + // Load text data from file + // NOTE: Expected an UTF-8 array of codepoints, no separation + char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName)); + codepoints = LoadCodepoints(textData, &codepointCount); + UnloadFileText(textData); } - else + + if (fontFileName[0] != '\0') { + // In case a font is already loaded and it is not default internal font, unload it if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); - font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); - if (font.texture.id == 0) font = GetFontDefault(); + + if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount); + else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); // Default to 95 standard codepoints } + // If font texture not properly loaded, revert to default font and size/spacing + if (font.texture.id == 0) + { + font = GetFontDefault(); + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); + } + + UnloadCodepoints(codepoints); + if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font); } break; @@ -3674,12 +4063,12 @@ void GuiLoadStyle(const char *fileName) if (fileDataSize > 0) { - unsigned char *fileData = (unsigned char *)RL_MALLOC(fileDataSize*sizeof(unsigned char)); + unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char)); fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); GuiLoadStyleFromMemory(fileData, fileDataSize); - RL_FREE(fileData); + RAYGUI_FREE(fileData); } fclose(rgsFile); @@ -3695,6 +4084,8 @@ void GuiLoadStyleDefault(void) guiStyleLoaded = true; // Initialize default LIGHT style property values + // WARNING: Default value are applied to all controls on set but + // they can be overwritten later on for every custom control GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); @@ -3707,17 +4098,29 @@ 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); // WARNING: Some controls use other values - GuiSetStyle(DEFAULT, TEXT_PADDING, 0); // WARNING: Some controls use other values - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); // WARNING: Some controls use other values + GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); + GuiSetStyle(DEFAULT, TEXT_PADDING, 0); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + // Initialize default extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15); // DEFAULT, 15 pixels between lines + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds // Initialize control-specific property values // NOTE: Those properties are in default list but require specific values by control type GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(BUTTON, BORDER_WIDTH, 2); GuiSetStyle(SLIDER, TEXT_PADDING, 4); + GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4); GuiSetStyle(CHECKBOX, TEXT_PADDING, 4); GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); + GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0); + GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); @@ -3729,10 +4132,6 @@ void GuiLoadStyleDefault(void) // Initialize extended property values // NOTE: By default, extended property values are initialized to 0 - GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls - GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls - GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property - GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property GuiSetStyle(TOGGLE, GROUP_PADDING, 2); GuiSetStyle(SLIDER, SLIDER_WIDTH, 16); GuiSetStyle(SLIDER, SLIDER_PADDING, 1); @@ -3742,8 +4141,6 @@ void GuiLoadStyleDefault(void) GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); - GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, (int)((float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f)); - GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24); GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2); GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); @@ -3767,12 +4164,17 @@ void GuiLoadStyleDefault(void) { // Unload previous font texture UnloadTexture(guiFont.texture); + RL_FREE(guiFont.recs); + RL_FREE(guiFont.glyphs); + guiFont.recs = NULL; + guiFont.glyphs = NULL; // Setup default raylib font guiFont = GetFontDefault(); // NOTE: Default raylib font character 95 is a white square Rectangle whiteChar = guiFont.recs[95]; + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); } @@ -3901,7 +4303,7 @@ void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) { #if !defined(RAYGUI_STANDALONE) - DrawRectangle(posX + (k%RAYGUI_ICON_SIZE)*pixelSize, posY + y*pixelSize, pixelSize, pixelSize, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color); #endif } @@ -3922,7 +4324,8 @@ void GuiSetIconScale(int scale) // Module specific Functions Definition //---------------------------------------------------------------------------------- -// Load style from memory (binary only) +// Load style from memory +// WARNING: Binary files only static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) { unsigned char *fileDataPtr = (unsigned char *)fileData; @@ -3977,7 +4380,6 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) { Font font = { 0 }; int fontType = 0; // 0-Normal, 1-SDF - Rectangle whiteRec = { 0 }; memcpy(&font.baseSize, fileDataPtr, sizeof(int)); memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int)); @@ -3985,7 +4387,8 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) fileDataPtr += 12; // Load font white rectangle - memcpy(&whiteRec, fileDataPtr, sizeof(Rectangle)); + Rectangle fontWhiteRec = { 0 }; + memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle)); fileDataPtr += 16; // Load font image parameters @@ -4002,7 +4405,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int)); fileDataPtr += 12; - if (fontImageCompSize < fontImageUncompSize) + if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize)) { // Compressed font atlas image data (DEFLATE), it requires DecompressData() int dataUncompSize = 0; @@ -4027,34 +4430,117 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); font.texture = LoadTextureFromImage(imFont); - if (font.texture.id == 0) font = GetFontDefault(); RAYGUI_FREE(imFont.data); - // Load font recs data - font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); - for (int i = 0; i < font.glyphCount; i++) + // Validate font atlas texture was loaded correctly + if (font.texture.id != 0) { - memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); - fileDataPtr += sizeof(Rectangle); - } + // Load font recs data + int recsDataSize = font.glyphCount*sizeof(Rectangle); + int recsDataCompressedSize = 0; - // Load font chars info data - font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); - for (int i = 0; i < font.glyphCount; i++) - { - memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int)); - memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int)); - memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int)); - memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int)); - fileDataPtr += 16; + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed recs data + memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize)) + { + // Recs data is compressed, uncompress it + unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize); + + memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize); + fileDataPtr += recsDataCompressedSize; + + int recsDataUncompSize = 0; + font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted"); + + RAYGUI_FREE(recsDataCompressed); + } + else + { + // Recs data is uncompressed + font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); + fileDataPtr += sizeof(Rectangle); + } + } + + // Load font glyphs info data + int glyphsDataSize = font.glyphCount*16; // 16 bytes data per glyph + int glyphsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed glyphs data + memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + // Allocate required glyphs space to fill with data + font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); + + if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize)) + { + // Glyphs data is compressed, uncompress it + unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize); + + memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize); + fileDataPtr += glyphsDataCompressedSize; + + int glyphsDataUncompSize = 0; + unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted"); + + unsigned char *glyphsDataUncompPtr = glyphsDataUncomp; + + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int)); + glyphsDataUncompPtr += 16; + } + + RAYGUI_FREE(glypsDataCompressed); + RAYGUI_FREE(glyphsDataUncomp); + } + else + { + // Glyphs data is uncompressed + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int)); + fileDataPtr += 16; + } + } } + else font = GetFontDefault(); // Fallback in case of errors loading font atlas texture GuiSetFont(font); // Set font texture source rectangle to be used as white texture to draw shapes - // NOTE: This way, all gui can be draw using a single draw call - if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); + // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call + if ((fontWhiteRec.x > 0) && + (fontWhiteRec.y > 0) && + (fontWhiteRec.width > 0) && + (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec); } #endif } @@ -4109,10 +4595,10 @@ static int GetTextWidth(const char *text) int codepoint = GetCodepointNext(&text[i], &codepointSize); int codepointIndex = GetGlyphIndex(guiFont, codepoint); - if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING)); + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); - textSize.x += glyphWidth; + textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); } } @@ -4130,15 +4616,23 @@ static Rectangle GetTextBounds(int control, Rectangle bounds) textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING); textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); - textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); // NOTE: Text is processed line per line! - // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT - // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) - // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER + // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds switch (control) { + case COMBOBOX: + case DROPDOWNBOX: + case LISTVIEW: + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW + case SLIDER: + case CHECKBOX: + case VALUEBOX: + case SPINNER: + // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER default: { + // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText() if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); else textBounds.x += GuiGetStyle(control, TEXT_PADDING); } @@ -4192,13 +4686,13 @@ const char **GetTextLines(const char *text, int *count) lines[0] = text; int len = 0; *count = 1; - int lineSize = 0; // Stores current line size, not returned + //int lineSize = 0; // Stores current line size, not returned for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { if (text[i] == '\n') { - lineSize = len; + //lineSize = len; k++; lines[k] = &text[i + 1]; // WARNING: next value is valid? len = 0; @@ -4212,8 +4706,37 @@ const char **GetTextLines(const char *text, int *count) return lines; } +// Get text width to next space for provided string +static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex) +{ + float width = 0; + int codepointByteCount = 0; + int codepoint = 0; + int index = 0; + float glyphWidth = 0; + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + for (int i = 0; text[i] != '\0'; i++) + { + if (text[i] != ' ') + { + codepoint = GetCodepoint(&text[i], &codepointByteCount); + index = GetGlyphIndex(guiFont, codepoint); + glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor; + width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + else + { + *nextSpaceIndex = i; + break; + } + } + + return width; +} + // Gui draw text using default font -static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint) { #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect @@ -4221,119 +4744,172 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color #define ICON_TEXT_PADDING 4 #endif - int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); + if ((text == NULL) || (text[0] == '\0')) return; // Security check - // We process the text lines one by one - if ((text != NULL) && (text[0] != '\0')) + // PROCEDURE: + // - Text is processed line per line + // - For every line, horizontal alignment is defined + // - For all text, vertical alignment is defined (multiline text only) + // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) + + // Get text lines (using '\n' as delimiter) to be processed individually + // WARNING: We can't use GuiTextSplit() function because it can be already used + // before the GuiDrawText() call and its buffer is static, it would be overriden :( + int lineCount = 0; + const char **lines = GetTextLines(text, &lineCount); + + // Text style variables + //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing + + // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float posOffsetY = 0.0f; + + for (int i = 0; i < lineCount; i++) { - // Get text lines ('\n' delimiter) to process lines individually - // NOTE: We can't use GuiTextSplit() because it can be already used before calling - // GuiDrawText() and static buffer would be overriden :( - int lineCount = 0; - const char **lines = GetTextLines(text, &lineCount); + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor - //Rectangle textBounds = GetTextBounds(LABEL, bounds); - float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); - float posOffsetY = 0; + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; - for (int i = 0; i < lineCount; i++) + // NOTE: We get text size after icon has been processed + // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed? + int textSizeX = GetTextWidth(lines[i]); + + // If text requires an icon, add size to measure + if (iconId >= 0) { - int iconId = 0; - lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; - // Get text position depending on alignment and iconId - //--------------------------------------------------------------------------------- - Vector2 position = { bounds.x, bounds.y }; - - // NOTE: We get text size after icon has been processed - // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed? - int textSizeX = GetTextWidth(lines[i]); - - // If text requires an icon, add size to measure - if (iconId >= 0) - { - textSizeX += RAYGUI_ICON_SIZE*guiIconScale; - - // WARNING: If only icon provided, text could be pointing to EOF character: '\0' - if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; - } - - // Check guiTextAlign global variables - switch (alignment) - { - case TEXT_ALIGN_LEFT: position.x = bounds.x; break; - case TEXT_ALIGN_CENTER: position.x = bounds.x + bounds.width/2 - textSizeX/2; break; - case TEXT_ALIGN_RIGHT: position.x = bounds.x + bounds.width - textSizeX; break; - default: break; - } - - switch (alignmentVertical) - { - case 0: position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // CENTERED - case 1: position.y = bounds.y + posOffsetY; break; // UP - case 2: position.y = bounds.y + posOffsetY + bounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // DOWN - 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) - //--------------------------------------------------------------------------------- + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' #if !defined(RAYGUI_NO_ICONS) - if (iconId >= 0) - { - // NOTE: We consider icon height, probably different than text size - GuiDrawIcon(iconId, (int)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint); - position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); - } + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; #endif - //DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + } - // Get size in bytes of text, - // considering end of line and line break - int size = 0; - for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n'); c++, size++){ } - float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + // Check guiTextAlign global variables + switch (alignment) + { + case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break; + case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x + textBounds.width/2 - textSizeX/2; break; + case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break; + default: break; + } - int textOffsetY = 0; - float textOffsetX = 0.0f; - for (int c = 0, codepointSize = 0; c < size; c += codepointSize) + switch (alignmentVertical) + { + // Only valid in case of wordWrap = 0; + case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break; + case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + default: break; + } + + // NOTE: Make sure we get pixel-perfect coordinates, + // In case of decimals we got weird text positioning + textBoundsPosition.x = (float)((int)textBoundsPosition.x); + textBoundsPosition.y = (float)((int)textBoundsPosition.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if !defined(RAYGUI_NO_ICONS) + if (iconId >= 0) + { + // NOTE: We consider icon height, probably different than text size + GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); + textBoundsPosition.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + } +#endif + // Get size in bytes of text, + // considering end of line and line break + int lineSize = 0; + for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ } + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + int textOffsetY = 0; + float textOffsetX = 0.0f; + float glyphWidth = 0; + for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize) + { + int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); + int index = GetGlyphIndex(guiFont, codepoint); + + // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol moving one byte + if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size + + // Wrap mode text measuring to space to validate if it can be drawn or + // a new line is required + if (wrapMode == TEXT_WRAP_CHAR) { - int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); - int index = GetGlyphIndex(guiFont, codepoint); + // Get glyph width to check if it goes out of bounds + if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor); + else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor; - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all of the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) codepointSize = 1; - - if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint - else + // Jump to next line if current character reach end of the box limits + if ((textOffsetX + glyphWidth) > textBounds.width) { - if ((codepoint != ' ') && (codepoint != '\t')) - { - // Draw only required text glyphs fitting the bounds.width - if (textOffsetX < (bounds.width - guiFont.recs[index].width)) - { - DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ position.x + textOffsetX, position.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), tint); - } - } - - if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); } } + else if (wrapMode == TEXT_WRAP_WORD) + { + // Get width to next space in line + int nextSpaceIndex = 0; + float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex); - // TODO: Allow users to set line spacing for text: GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, x) - posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; - //--------------------------------------------------------------------------------- + if ((textOffsetX + nextSpaceWidth) > textBounds.width) + { + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + } + + // TODO: Consider case: (nextSpaceWidth >= textBounds.width) + } + + if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint + else + { + // TODO: There are multiple types of spaces in Unicode, + // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html + if ((codepoint != ' ') && (codepoint != '\t')) // Do not draw codepoints with no glyph + { + if (wrapMode == TEXT_WRAP_NONE) + { + // Draw only required text glyphs fitting the textBounds.width + if (textOffsetX <= (textBounds.width - glyphWidth)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) + { + // Draw only glyphs inside the bounds + if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + + if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } } + + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + //--------------------------------------------------------------------------------- } + #if defined(RAYGUI_DEBUG_TEXT_BOUNDS) - GuiDrawRectangle(bounds, 0, WHITE, Fade(RED, 0.4f)); + GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f)); #endif } @@ -4343,17 +4919,21 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, if (color.a > 0) { // Draw rectangle filled with color - DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, color); + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha)); } if (borderWidth > 0) { // Draw rectangle border lines with color - DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, borderColor); - DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); - DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); - DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, borderColor); + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); } + +#if defined(RAYGUI_DEBUG_RECS_BOUNDS) + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f)); +#endif } // Draw tooltip using control bounds @@ -4629,12 +5209,16 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) if (guiSliderDragging) // Keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + !CheckCollisionPointRec(mousePoint, arrowUpLeft) && + !CheckCollisionPointRec(mousePoint, arrowDownRight)) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { - if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); - else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); + state = STATE_PRESSED; + + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); } } else @@ -4646,8 +5230,6 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) else if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - guiSliderDragging = true; - guiSliderActive = bounds; // Store bounds as an identifier when dragging starts // Handle mouse wheel int wheel = (int)GetMouseWheelMove(); @@ -4656,6 +5238,9 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) // Handle mouse button down if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts + // Check arrows click if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); @@ -4668,11 +5253,6 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) state = STATE_PRESSED; } - else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) - { - if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); - else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); - } // Keyboard control on mouse hover scrollbar /* @@ -4697,10 +5277,10 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) // Draw control //-------------------------------------------------------------------- - 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(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED))); // 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 + GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL))); // Draw the scrollbar active area background + GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3))); // Draw the slider bar // Draw arrows (using icon if available) if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) @@ -4708,17 +5288,17 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) #if defined(RAYGUI_NO_ICONS) GuiDrawText(isVertical? "^" : "<", RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); GuiDrawText(isVertical? "v" : ">", RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); #else GuiDrawText(isVertical? "#121#" : "#118#", RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL GuiDrawText(isVertical? "#120#" : "#119#", RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL #endif } //-------------------------------------------------------------------- @@ -4726,6 +5306,18 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) return value; } +// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +// WARNING: It multiplies current alpha by alpha scale factor +static Color GuiFade(Color color, float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) }; + + return result; +} + #if defined(RAYGUI_STANDALONE) // Returns a Color struct from hexadecimal value static Color GetColor(int hexValue) @@ -4757,17 +5349,6 @@ static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) return collision; } -// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f -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 result; -} - // Formatting of text with variables to 'embed' static const char *TextFormat(const char *text, ...) {