diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 3277d7eaa..3fc2e43e0 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,18 +1,21 @@ /******************************************************************************************* * -* raygui v2.0-dev - A simple and easy-to-use immedite-mode-gui library +* raygui v2.5 - A simple and easy-to-use immediate-mode-gui library * * DESCRIPTION: * * raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also possible * to be used as a standalone library, as long as input and drawing functions are provided. * -* Basic controls provided: +* Controls provided: * +* # Container/separators Controls * - WindowBox * - GroupBox * - Line * - Panel +* +* # Basic Controls * - Label * - Button * - LabelButton --> Label @@ -31,14 +34,17 @@ * - SliderBar --> Slider * - ProgressBar * - StatusBar +* - ScrollBar * - ScrollPanel -* - ListView --> ListElement -* - ColorPicker --> ColorPanel, ColorBarHue -* - MessageBox * - DummyRec -* - ScrollBar * - Grid * +* # Advance Controls +* - ListView --> ListElement +* - ColorPicker --> ColorPanel, ColorBarHue +* - MessageBox --> Label, Button +* - TextInputBox --> Label, TextBox, Button +* * It also provides a set of functions for styling the controls based on its properties (size, color). * * CONFIGURATION: @@ -61,9 +67,20 @@ * Includes ricons.h header defining a set of 128 icons (binary format) to be used on * multiple controls and following raygui styles * +* #define RAYGUI_TEXTBOX_EXTENDED +* Enables the advance GuiTextBox()/GuiValueBox()/GuiSpinner() implementation with +* text selection support and text copy/cut/paste support +* * VERSIONS HISTORY: -* 2.0 (xx-Dec-2018) Complete review of new controls, redesigned style system -* 1.9 (01-May-2018) Lot of rework and redesign! Lots of new controls! +* 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner() +* 2.3 (29-Apr-2019) Added rIcons auxiliar library and support for it, multiple controls reviewed +* Refactor all controls drawing mechanism to use control state +* 2.2 (05-Feb-2019) Added GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls +* 2.1 (26-Dec-2018) Redesign of GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string +* Complete redesign of style system (breaking change) +* 2.0 (08-Nov-2018) Support controls guiLock and custom fonts, reviewed GuiComboBox(), GuiListView()... +* 1.9 (09-Oct-2018) Controls review: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()... +* 1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout * 1.5 (21-Jun-2017) Working in an improved styles system * 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones) * 1.3 (12-Jun-2017) Redesigned styles system @@ -74,6 +91,7 @@ * * 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) @@ -85,7 +103,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -107,7 +125,8 @@ #ifndef RAYGUI_H #define RAYGUI_H -#define RAYGUI_VERSION "2.0-dev" +#define RAYGUI_VERSION "2.5-dev" +#define RAYGUI_RICONS_SUPPORT #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -134,11 +153,9 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define VALIGN_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect - #define TEXTEDIT_CURSOR_BLINK_FRAMES 20 // Text edit controls cursor blink timming -#define NUM_CONTROLS 13 // Number of standard controls +#define NUM_CONTROLS 16 // Number of standard controls #define NUM_PROPS_DEFAULT 16 // Number of standard properties #define NUM_PROPS_EXTENDED 8 // Number of extended properties @@ -182,15 +199,27 @@ int width; int height; } Rectangle; - + // Texture2D type - typedef struct Texture2D { } Texture2D; - + // NOTE: It should be provided by user + typedef struct Texture2D Texture2D; + // Font type - typedef struct Font { } Font; + // NOTE: It should be provided by user + typedef struct Font Font; +#endif + +#if defined(RAYGUI_TEXTBOX_EXTENDED) +// Gui text box state data +typedef struct GuiTextBoxState { + int cursor; // Cursor position in text + int start; // Text start position (from where we begin drawing the text) + int index; // Text start index (index inside the text of `start` always in sync) + int select; // Marks position of cursor when selection has started +} GuiTextBoxState; #endif -// Gui global state enum +// Gui control state typedef enum { GUI_STATE_NORMAL = 0, GUI_STATE_FOCUSED, @@ -198,14 +227,14 @@ typedef enum { GUI_STATE_DISABLED, } GuiControlState; -// Gui global text alignment +// Gui control text alignment typedef enum { GUI_TEXT_ALIGN_LEFT = 0, GUI_TEXT_ALIGN_CENTER, GUI_TEXT_ALIGN_RIGHT, } GuiTextAlignment; -// Gui standard controls +// Gui controls typedef enum { DEFAULT = 0, LABEL, // LABELBUTTON @@ -216,13 +245,16 @@ typedef enum { CHECKBOX, COMBOBOX, DROPDOWNBOX, - TEXTBOX, // VALUEBOX, SPINNER, TEXTBOXMULTI -> TODO: Probably they should not be dependant on TEXTBOX style! + TEXTBOX, // TEXTBOXMULTI + VALUEBOX, + SPINNER, LISTVIEW, COLORPICKER, - SCROLLBAR -} GuiControlStandard; + SCROLLBAR, + RESERVED +} GuiControl; -// Gui default properties for every control +// Gui base properties for every control typedef enum { BORDER_COLOR_NORMAL = 0, BASE_COLOR_NORMAL, @@ -242,15 +274,14 @@ typedef enum { RESERVED02 } GuiControlProperty; -// Gui extended properties depending on control type -// NOTE: We reserve a fixed size of additional properties per control (8) +// Gui extended properties depend on control +// NOTE: We reserve a fixed size of additional properties per control -// Default properties +// DEFAULT properties typedef enum { TEXT_SIZE = 16, TEXT_SPACING, LINE_COLOR, - //LINE_THICK, BACKGROUND_COLOR, } GuiDefaultProperty; @@ -274,14 +305,6 @@ typedef enum { // ProgressBar //typedef enum { } GuiProgressBarProperty; -// TextBox / TextBoxMulti / ValueBox / Spinner -typedef enum { - MULTILINE_PADDING = 16, - SPINNER_BUTTON_WIDTH, - SPINNER_BUTTON_PADDING, - SPINNER_BUTTON_BORDER_WIDTH -} GuiTextBoxProperty; - // CheckBox typedef enum { CHECK_TEXT_PADDING = 16 @@ -298,22 +321,18 @@ typedef enum { ARROW_RIGHT_PADDING = 16, } GuiDropdownBoxProperty; -// ColorPicker +// TextBox / TextBoxMulti / ValueBox / Spinner typedef enum { - COLOR_SELECTOR_SIZE = 16, - BAR_WIDTH, // Lateral bar width - BAR_PADDING, // Lateral bar separation from panel - BAR_SELECTOR_HEIGHT, // Lateral bar selector height - BAR_SELECTOR_PADDING // Lateral bar selector outer padding -} GuiColorPickerProperty; + MULTILINE_PADDING = 16, + COLOR_SELECTED_FG, + COLOR_SELECTED_BG +} GuiTextBoxProperty; -// ListView typedef enum { - ELEMENTS_HEIGHT = 16, - ELEMENTS_PADDING, - SCROLLBAR_WIDTH, - SCROLLBAR_SIDE, // This property defines vertical scrollbar side (SCROLLBAR_LEFT_SIDE or SCROLLBAR_RIGHT_SIDE) -} GuiListViewProperty; + SELECT_BUTTON_WIDTH = 16, + SELECT_BUTTON_PADDING, + SELECT_BUTTON_BORDER_WIDTH +} GuiSpinnerProperty; // ScrollBar typedef enum { @@ -321,7 +340,7 @@ typedef enum { SLIDER_PADDING, SLIDER_SIZE, SCROLL_SPEED, - SHOW_SPINNER_BUTTONS + ARROWS_VISIBLE } GuiScrollBarProperty; // ScrollBar side @@ -330,6 +349,23 @@ typedef enum { SCROLLBAR_RIGHT_SIDE } GuiScrollBarSide; +// ListView +typedef enum { + ELEMENTS_HEIGHT = 16, + ELEMENTS_PADDING, + SCROLLBAR_WIDTH, + SCROLLBAR_SIDE, // This property defines vertical scrollbar side (SCROLLBAR_LEFT_SIDE or SCROLLBAR_RIGHT_SIDE) +} GuiListViewProperty; + +// ColorPicker +typedef enum { + COLOR_SELECTOR_SIZE = 16, + BAR_WIDTH, // Lateral bar width + BAR_PADDING, // Lateral bar separation from panel + BAR_SELECTOR_HEIGHT, // Lateral bar selector height + BAR_SELECTOR_PADDING // Lateral bar selector outer padding +} GuiColorPickerProperty; + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -352,6 +388,25 @@ RAYGUIDEF void GuiFade(float alpha); // Set g RAYGUIDEF void GuiSetStyle(int control, int property, int value); // Set one style property RAYGUIDEF int GuiGetStyle(int control, int property); // Get one style property +#if defined(RAYGUI_TEXTBOX_EXTENDED) +// GuiTextBox() extended functions +RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds); // Sets the active textbox +RAYGUIDEF Rectangle GuiTextBoxGetActive(void); // Get bounds of active textbox +RAYGUIDEF void GuiTextBoxSetCursor(int cursor); // Set cursor position of active textbox +RAYGUIDEF int GuiTextBoxGetCursor(void); // Get cursor position of active textbox +RAYGUIDEF void GuiTextBoxSetSelection(int start, int length); // Set selection of active textbox +RAYGUIDEF Vector2 GuiTextBoxGetSelection(void); // Get selection of active textbox (x - selection start y - selection length) +RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds); // Returns true if a textbox control with specified `bounds` is the active textbox +RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void); // Get state for the active textbox +RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state); // Set state for the active textbox (state must be valid else things will break) +RAYGUIDEF void GuiTextBoxSelectAll(const char *text); // Select all characters in the active textbox (same as pressing `CTRL` + `A`) +RAYGUIDEF void GuiTextBoxCopy(const char *text); // Copy selected text to clipboard from the active textbox (same as pressing `CTRL` + `C`) +RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize); // Paste text from clipboard into the textbox (same as pressing `CTRL` + `V`) +RAYGUIDEF void GuiTextBoxCut(char *text); // Cut selected text in the active textbox and copy it to clipboard (same as pressing `CTRL` + `X`) +RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before); // Deletes a character or selection before from the active textbox (depending on `before`). Returns bytes deleted. +RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to); // Get the byte index for a character starting at position `from` with index `start` until position `to`. +#endif + // Container/separator controls, useful for controls organization RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text); // Window Box control, shows a window that can be closed RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with title name @@ -380,13 +435,14 @@ RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, RAYGUIDEF void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text RAYGUIDEF void GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll Bar control +RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs); // Grid control // Advance controls set RAYGUIDEF bool GuiListView(Rectangle bounds, const char *text, int *active, int *scrollIndex, bool editMode); // List View control, returns selected list element index RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int *enabled, int *active, int *focus, int *scrollIndex, bool editMode); // List View with extended parameters RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const char *message, const char *buttons); // Message Box control, displays a message +RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *windowTitle, const char *message, char *text, const char *buttons); // Text Input Box control, ask for text RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color); // Color Picker control -RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs); // Grid // Styles loading functions RAYGUIDEF void GuiLoadStyle(const char *fileName); // Load style file (.rgs) @@ -417,7 +473,7 @@ RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text wit #if defined(RAYGUI_STANDALONE) #define RICONS_STANDALONE #endif - + #define RICONS_IMPLEMENTATION #include "ricons.h" // Required for: raygui icons #endif @@ -429,6 +485,12 @@ RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text wit #include // Required for: va_list, va_start(), vfprintf(), va_end() #endif +#ifdef __cplusplus + #define RAYGUI_CLITERAL(name) name +#else + #define RAYGUI_CLITERAL(name) (name) +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -456,6 +518,11 @@ static float guiAlpha = 1.0f; static unsigned int guiStyle[NUM_CONTROLS*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED)] = { 0 }; static bool guiStyleLoaded = false; +#if defined(RAYGUI_TEXTBOX_EXTENDED) +static Rectangle guiTextBoxActive = { 0 }; // Area of the currently active textbox +static GuiTextBoxState guiTextBoxState = { .cursor = -1, .start = 0, .index = 0, .select = -1 }; // Keeps state of the active textbox +#endif + //---------------------------------------------------------------------------------- // Standalone Mode Functions Declaration // @@ -472,63 +539,47 @@ static bool guiStyleLoaded = false; #define KEY_ENTER 257 #define MOUSE_LEFT_BUTTON 0 -#ifdef __cplusplus - #define CLITERAL -#else - #define CLITERAL (Color) -#endif - -#define WHITE CLITERAL{ 255, 255, 255, 255 } // White -#define BLACK CLITERAL{ 0, 0, 0, 255 } // Black -#define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo) -#define GRAY CLITERAL{ 130, 130, 130, 255 } // Gray -- GuiColorBarAlpha() - -// raylib functions are 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' -//------------------------------------------------------------------------------- - // Input required functions //------------------------------------------------------------------------------- -static Vector2 GetMousePosition(void) { return (Vector2){ 0, 0 }; } -static int GetMouseWheelMove(void) { return 0; } -static bool IsMouseButtonDown(int button) { return false; } -static bool IsMouseButtonPressed(int button) { return false; } -static bool IsMouseButtonReleased(int button) { return false; } - -static bool IsKeyDown(int key) { return false; } -static bool IsKeyPressed(int key) { return false; } -static int GetKeyPressed(void) { return 0; } // -- GuiTextBox() +static Vector2 GetMousePosition(void); +static int GetMouseWheelMove(void); +static bool IsMouseButtonDown(int button); +static bool IsMouseButtonPressed(int button); +static bool IsMouseButtonReleased(int button); + +static bool IsKeyDown(int key); +static bool IsKeyPressed(int key); +static int GetKeyPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox() //------------------------------------------------------------------------------- // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color) { /* TODO */ } -static void DrawRectangleRec(Rectangle rec, Color color) { DrawRectangle(rec.x, rec.y, rec.width, rec.height, color); } - -static void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) { /* TODO */ } - -static void DrawRectangleLines(int x, int y, int width, int height, Color color) { /* TODO */ } // -- GuiColorPicker() -static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // -- GuiColorPicker() -static void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2); // -- GuiColorPicker() -static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +static void DrawRectangle(int x, int y, int width, int height, Color color); +static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +static void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // -- GuiDropdownBox(), GuiScrollBar() +static void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // -- GuiImageButtonEx() +//------------------------------------------------------------------------------- -static void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { /* TODO */ } // -- GuiDropdownBox() -static void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) { /* TODO */ } // -- GuiScrollBar() +// Text required functions +//------------------------------------------------------------------------------- +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // -- GetTextWidth(), GuiTextBoxMulti() +static void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // -- GuiDrawText() -static void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint) { } // -- GuiImageButtonEx() +static Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount); // -- GuiLoadStyle() //------------------------------------------------------------------------------- -// Text required functions +// raylib functions already implemented in raygui //------------------------------------------------------------------------------- -static Font GetFontDefault(void); // -- GetTextWidth() +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 Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing) { return (Vector2){ 0.0f }; } // Measure text size depending on font -static void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint) { } // Draw text using font and additional parameters +static void DrawRectangleRec(Rectangle rec, Color color); // Draw rectangle filled with color +static void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outlines +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient //------------------------------------------------------------------------------- #endif // RAYGUI_STANDALONE @@ -548,8 +599,6 @@ static int GetTextWidth(const char *text) // TODO: GetTextSize() { Vector2 size = { 0 }; - if (guiFont.texture.id == 0) guiFont = GetFontDefault(); - if ((text != NULL) && (text[0] != '\0')) size = MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING)); // TODO: Consider text icon width here??? @@ -573,8 +622,9 @@ static Rectangle GetTextBounds(int control, Rectangle bounds) case CHECKBOX: bounds.x += (bounds.width + GuiGetStyle(control, CHECK_TEXT_PADDING)); break; default: break; } - // TODO: Special cases: COMBOBOX, DROPDOWNBOX, SPINNER, LISTVIEW (scrollbar?) - // More special cases: CHECKBOX, SLIDER + + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, SPINNER, LISTVIEW (scrollbar?) + // More special cases (label side): CHECKBOX, SLIDER return textBounds; } @@ -609,7 +659,7 @@ static const char *GetTextIcon(const char *text, int *iconId) // Gui draw text using default font static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) { - if (guiFont.texture.id == 0) guiFont = GetFontDefault(); + #define VALIGN_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect if ((text != NULL) && (text[0] != '\0')) { @@ -635,7 +685,6 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color if ((text != NULL) && (text[0] != '\0')) textWidth += ICON_TEXT_PADDING; } #endif - // Check guiTextAlign global variables switch (alignment) { @@ -666,7 +715,7 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color if (iconId > 0) { // NOTE: We consider icon height, probably different than text size - DrawIcon(iconId, (Vector2){ position.x, bounds.y + bounds.height/2 - RICONS_SIZE/2 + VALIGN_OFFSET(bounds.height) }, 1, tint); + DrawIcon(iconId, RAYGUI_CLITERAL(Vector2){ position.x, bounds.y + bounds.height/2 - RICONS_SIZE/2 + VALIGN_OFFSET(bounds.height) }, 1, tint); position.x += (RICONS_SIZE + ICON_TEXT_PADDING); } #endif @@ -705,12 +754,6 @@ RAYGUIDEF void GuiFont(Font font) { guiFont = font; GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); - - // Populate all controls with new font size - for (int i = 1; i < NUM_CONTROLS; i++) GuiSetStyle(i, TEXT_SIZE, GuiGetStyle(DEFAULT, TEXT_SIZE)); - - // NOTE: Loaded font spacing must be set manually - //GuiSetStyle(DEFAULT, TEXT_SPACING, 1); } } @@ -737,6 +780,61 @@ RAYGUIDEF int GuiGetStyle(int control, int property) return guiStyle[control*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property]; } +#if defined(RAYGUI_TEXTBOX_EXTENDED) +// Sets the active textbox (reseting state of the previous active textbox) +RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds) +{ + guiTextBoxActive = bounds; + guiTextBoxState = (GuiTextBoxState){ .cursor = -1, .start = 0, .index = 0, .select = -1 }; +} + +// Gets bounds of active textbox +RAYGUIDEF Rectangle GuiTextBoxGetActive(void) { return guiTextBoxActive; } + +// Set cursor position of active textbox +RAYGUIDEF void GuiTextBoxSetCursor(int cursor) +{ + guiTextBoxState.cursor = (cursor < 0) ? -1 : cursor; + guiTextBoxState.start = -1; // Mark this to be recalculated +} + +// Get cursor position of active textbox +RAYGUIDEF int GuiTextBoxGetCursor(void) { return guiTextBoxState.cursor; } + +// Set selection of active textbox +RAYGUIDEF void GuiTextBoxSetSelection(int start, int length) +{ + if(start < 0) start = 0; + if(length < 0) length = 0; + GuiTextBoxSetCursor(start + length); + guiTextBoxState.select = start; +} + +// Get selection of active textbox +RAYGUIDEF Vector2 GuiTextBoxGetSelection(void) +{ + if(guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor) + return RAYGUI_CLITERAL(Vector2){ 0 }; + else if(guiTextBoxState.cursor > guiTextBoxState.select) + return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.select, guiTextBoxState.cursor - guiTextBoxState.select }; + + return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.cursor, guiTextBoxState.select - guiTextBoxState.cursor }; +} + +// Returns true if a textbox control with specified `bounds` is the active textbox +RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds) +{ + return (bounds.x == guiTextBoxActive.x && bounds.y == guiTextBoxActive.y && + bounds.width == guiTextBoxActive.width && bounds.height == guiTextBoxActive.height); +} +RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void) { return guiTextBoxState; } +RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state) +{ + // NOTE: should we check if state values are valid ?!? + guiTextBoxState = state; +} +#endif + // Window Box control RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) { @@ -761,7 +859,7 @@ RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) // Draw window base DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, BORDER + (state*3))), guiAlpha)); - DrawRectangleRec((Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 }, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); @@ -775,8 +873,8 @@ RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, defaultTextAlign); // Draw window close button - int buttonBorder = GuiGetStyle(BUTTON, BORDER_WIDTH); - int buttonTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); #if defined(RAYGUI_RICONS_SUPPORT) @@ -784,8 +882,8 @@ RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) #else clicked = GuiButton(buttonRec, "x"); #endif - GuiSetStyle(BUTTON, BORDER_WIDTH, buttonBorder); - GuiSetStyle(BUTTON, TEXT_ALIGNMENT, buttonTextAlignment); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); //-------------------------------------------------------------------- return clicked; @@ -806,7 +904,7 @@ RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text) DrawRectangle(bounds.x, bounds.y + bounds.height - 1, bounds.width, GROUPBOX_LINE_THICK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); DrawRectangle(bounds.x + bounds.width - 1, bounds.y, GROUPBOX_LINE_THICK, bounds.height, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - GuiLine((Rectangle){ bounds.x, bounds.y, bounds.width, 1 }, text); + GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, 1 }, text); //-------------------------------------------------------------------- } @@ -871,13 +969,13 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 const int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; const int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; - const Rectangle horizontalScrollBar = { ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? bounds.x + verticalScrollBarWidth : bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), horizontalScrollBarWidth }; - const Rectangle verticalScrollBar = { ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), verticalScrollBarWidth, bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; + const Rectangle horizontalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)horizontalScrollBarWidth }; + const Rectangle verticalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)verticalScrollBarWidth, (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; // Calculate view area (area without the scrollbars) Rectangle view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? - (Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } : - (Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth }; + RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } : + RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth }; // Clip view area to the actual content size if (view.width > content.width) view.width = content.width; @@ -912,7 +1010,7 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); } - + scrollPos.y += GetMouseWheelMove()*20; } } @@ -946,22 +1044,22 @@ RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 GuiSetStyle(SCROLLBAR, SLIDER_SIZE, ((bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/content.height)* (bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)); scrollPos.y = -GuiScrollBar(verticalScrollBar, -scrollPos.y, verticalMin, verticalMax); } - + // Draw detail corner rectangle if both scroll bars are visible - if (hasHorizontalScrollBar && hasVerticalScrollBar) + if (hasHorizontalScrollBar && hasVerticalScrollBar) { // TODO: Consider scroll bars side - DrawRectangle(horizontalScrollBar.x + horizontalScrollBar.width + 2, - verticalScrollBar.y + verticalScrollBar.height + 2, - horizontalScrollBarWidth - 4, verticalScrollBarWidth - 4, - Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))), guiAlpha)); + DrawRectangle(horizontalScrollBar.x + horizontalScrollBar.width + 2, + verticalScrollBar.y + verticalScrollBar.height + 2, + horizontalScrollBarWidth - 4, verticalScrollBarWidth - 4, + Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state * 3))), guiAlpha)); } // Set scrollbar slider size back to the way it was before GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider); // Draw scrollbar lines depending on current state - DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, (float)BORDER + (state*3))), guiAlpha)); //-------------------------------------------------------------------- if (scroll != NULL) *scroll = scrollPos; @@ -1053,7 +1151,7 @@ RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text) // Image button control, returns true when clicked RAYGUIDEF bool GuiImageButton(Rectangle bounds, Texture2D texture) { - return GuiImageButtonEx(bounds, texture, (Rectangle){ 0, 0, texture.width, texture.height }, NULL); + return GuiImageButtonEx(bounds, texture, RAYGUI_CLITERAL(Rectangle){ 0, 0, (float)texture.width, (float)texture.height }, NULL); } // Image button control, returns true when clicked @@ -1084,7 +1182,7 @@ RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle t DrawRectangle(bounds.x + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.y + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); if (text != NULL) GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); - if (texture.id > 0) DrawTextureRec(texture, texSource, (Vector2){ bounds.x + bounds.width/2 - (texSource.width + GuiGetStyle(BUTTON, INNER_PADDING)/2)/2, bounds.y + bounds.height/2 - texSource.height/2 }, Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); + if (texture.id > 0) DrawTextureRec(texture, texSource, RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width/2 - (texSource.width + GuiGetStyle(BUTTON, INNER_PADDING)/2)/2, bounds.y + bounds.height/2 - texSource.height/2 }, Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); //------------------------------------------------------------------ return clicked; @@ -1184,7 +1282,7 @@ RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) Vector2 mousePoint = GetMousePosition(); // Check checkbox state - if (CheckCollisionPointRec(mousePoint, (Rectangle){ bounds.x, bounds.y, bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING), bounds.height })) + if (CheckCollisionPointRec(mousePoint, RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING), bounds.height })) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; else state = GUI_STATE_FOCUSED; @@ -1217,8 +1315,8 @@ RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) bounds.width -= (GuiGetStyle(COMBOBOX, SELECTOR_WIDTH) + GuiGetStyle(COMBOBOX, SELECTOR_PADDING)); - Rectangle selector = { bounds.x + bounds.width + GuiGetStyle(COMBOBOX, SELECTOR_PADDING), - bounds.y, GuiGetStyle(COMBOBOX, SELECTOR_WIDTH), bounds.height }; + Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, SELECTOR_PADDING), + (float)bounds.y, (float)GuiGetStyle(COMBOBOX, SELECTOR_WIDTH), (float)bounds.height }; // Get substrings elements from text (elements pointers, lengths and count) int elementsCount = 0; @@ -1258,14 +1356,15 @@ RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) // Draw selector using a custom button // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values - GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); GuiButton(selector, TextFormat("%i/%i", active + 1, elementsCount)); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); - GuiSetStyle(BUTTON, BORDER_WIDTH, 2); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); //-------------------------------------------------------------------- return active; @@ -1310,89 +1409,1101 @@ RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, b } else { - if (CheckCollisionPointRec(mousePoint, closeBounds)) + if (CheckCollisionPointRec(mousePoint, closeBounds)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + } + else if (!CheckCollisionPointRec(mousePoint, openBounds)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + + // TODO: Review this ugly hack... DROPDOWNBOX depends on GuiListElement() that uses DEFAULT_TEXT_ALIGNMENT + int tempTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT)); + + switch (state) + { + case GUI_STATE_NORMAL: + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_NORMAL)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_NORMAL)), guiAlpha)); + + GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); + } break; + case GUI_STATE_FOCUSED: + { + GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, editMode); + } break; + case GUI_STATE_PRESSED: + { + if (!editMode) GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); + if (editMode) + { + GuiPanel(openBounds); + + GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); + + for (int i = 0; i < elementsCount; i++) + { + if (i == auxActive && editMode) + { + if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height*(i + 1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), + bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, + elementsPtrs[i], true, true) == false) pressed = true; + } + else + { + if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height*(i+1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), + bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, + elementsPtrs[i], false, true)) + { + auxActive = i; + pressed = true; + } + } + } + } + } break; + case GUI_STATE_DISABLED: + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); + } break; + default: break; + } + + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, tempTextAlign); + + // TODO: Avoid this function, use icon instead or 'v' + DrawTriangle(RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2 }, + RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 5, bounds.y + bounds.height/2 - 2 + 5 }, + RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 10, bounds.y + bounds.height/2 - 2 }, + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + + //GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, + // GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + *active = auxActive; + return pressed; +} + +#if defined(RAYGUI_TEXTBOX_EXTENDED) +// Spinner control, returns selected value +// NOTE: Requires static variables: timer, valueSpeed - ERROR! +RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) +{ + #define GUI_SPINNER_HOLD_SPEED 0.2f // Min 200ms delay + + static float timer = 0.0f; + + int tempValue = *value; + const float time = GetTime(); // Get current time + bool pressed = false, active = GuiTextBoxIsActive(bounds); + + Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING), bounds.y, + bounds.width - 2*(GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING)), bounds.height }; + Rectangle leftButtonBound = { bounds.x, bounds.y, GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.height }; + Rectangle rightButtonBound = { bounds.x + bounds.width - GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.y, GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.height }; + + // Update control + //-------------------------------------------------------------------- + Vector2 mouse = GetMousePosition(); + if (tempValue < minValue) tempValue = minValue; + if (tempValue > maxValue) tempValue = maxValue; + + if (editMode) + { + if (!active) + { + // This becomes the active textbox when mouse is pressed or held inside bounds + if ((IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonDown(MOUSE_LEFT_BUTTON)) && + CheckCollisionPointRec(mouse, bounds)) + { + GuiTextBoxSetActive(bounds); + active = true; + } + } + } + + // Reset timer when one of the buttons is clicked (without this, holding the button down will not behave correctly) + if ((CheckCollisionPointRec(mouse, leftButtonBound) || CheckCollisionPointRec(mouse, rightButtonBound)) && + IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + timer = time; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (GuiTextBoxIsActive(bounds)) guiTextBoxActive = spinner; // Set our spinner as the active textbox + pressed = GuiValueBox(spinner, &tempValue, minValue, maxValue, editMode); + if (GuiTextBoxIsActive(spinner)) guiTextBoxActive = bounds; // Revert change + + // Draw value selector custom buttons + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH)); + + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + + char *icon = "<"; +#if defined(RAYGUI_RICONS_SUPPORT) + icon = (char *)GuiIconText(RICON_ARROW_LEFT_FILL, NULL); +#endif + if (GuiButton(leftButtonBound, icon) || // NOTE: also decrease value when the button is held down + (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + CheckCollisionPointRec(mouse, leftButtonBound) && + (time - timer) > GUI_SPINNER_HOLD_SPEED)) + { + tempValue--; + } + + icon = ">"; +#if defined(RAYGUI_RICONS_SUPPORT) + icon = (char *)GuiIconText(RICON_ARROW_RIGHT_FILL, NULL); +#endif + if (GuiButton(rightButtonBound, icon) || // NOTE: also increase value when the button is held down + (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + CheckCollisionPointRec(mouse, rightButtonBound) && + (time - timer) > GUI_SPINNER_HOLD_SPEED)) + { + tempValue++; + } + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + //-------------------------------------------------------------------- + + if (tempValue < minValue) tempValue = minValue; + if (tempValue > maxValue) tempValue = maxValue; + + // Reset timer + if (active && (((time - timer) > GUI_SPINNER_HOLD_SPEED) || (timer == 0.0f) || (timer > time))) timer = time; + + *value = tempValue; + + return pressed; +} + +// Value Box control, updates input text with numbers +RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) +{ + #define VALUEBOX_MAX_CHARS 32 + + char text[VALUEBOX_MAX_CHARS + 1] = { 0 }; + sprintf(text, "%i", *value); + + bool pressed = GuiTextBox(bounds, text, VALUEBOX_MAX_CHARS, editMode); + *value = atoi(text); + + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + + return pressed; +} + +enum { + GUI_MEASURE_MODE_CURSOR_END = 0xA, + GUI_MEASURE_MODE_CURSOR_POS, + GUI_MEASURE_MODE_CURSOR_COORDS, +}; + +// Required by GuiTextBox() +// Highly synchronized with calculations in DrawTextRecEx() +static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, int *pos, int mode) +{ + // Get gui font properties + const Font font = guiFont; + const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); + const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); + + int textOffsetX = 0; // Offset between characters + float scaleFactor = 0.0f; + + int letter = 0; // Current character + int index = 0; // Index position in sprite font + + scaleFactor = fontSize/font.baseSize; + + int i = 0, k = 0; + int glyphWidth = 0; + for (i = 0; i < length; i++, k++) + { + glyphWidth = 0; + int next = 1; + letter = GetNextCodepoint(&text[i], &next); + if (letter == 0x3f) next = 1; + index = GetGlyphIndex(font, letter); + i += next - 1; + + if (letter != '\n') + { + glyphWidth = (font.chars[index].advanceX == 0)? + (int)(font.chars[index].rec.width*scaleFactor + spacing): + (int)(font.chars[index].advanceX*scaleFactor + spacing); + + if ((textOffsetX + glyphWidth + 1) >= rec.width) break; + + if ((mode == GUI_MEASURE_MODE_CURSOR_POS) && (*pos == k)) break; + else if (mode == GUI_MEASURE_MODE_CURSOR_COORDS) + { + // Check if the mouse pointer is inside the glyph rect + Rectangle grec = {rec.x + textOffsetX - 1, rec.y, glyphWidth, (font.baseSize + font.baseSize/2)*scaleFactor - 1 }; + Vector2 mouse = GetMousePosition(); + + if (CheckCollisionPointRec(mouse, grec)) + { + // Smooth selection by dividing the glyph rectangle into 2 equal parts and checking where the mouse resides + if (mouse.x > (grec.x + glyphWidth/2)) + { + textOffsetX += glyphWidth; + k++; + } + + break; + } + } + } + else break; + + textOffsetX += glyphWidth; + } + + *pos = k; + + return (rec.x + textOffsetX - 1); +} + +static int GetPrevCodepoint(const char *text, const char *start, int *prev) +{ + int c = 0x3f; + char *p = (char *)text; + *prev = 1; + + for (int i = 0; (p >= start) && (i < 4); p--, i++) + { + if ((((unsigned char)*p) >> 6) != 2) + { + c = GetNextCodepoint(p, prev); + break; + } + } + + return c; +} + +// Required by GuiTextBoxEx() +// Highly synchronized with calculations in DrawTextRecEx() +static int GuiMeasureTextBoxRev(const char *text, int length, Rectangle rec, int *pos) +{ + // Get gui font properties + const Font font = guiFont; + const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE); + const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING); + + int textOffsetX = 0; // Offset between characters + float scaleFactor = 0.0f; + + int letter = 0; // Current character + int index = 0; // Index position in sprite font + + scaleFactor = fontSize/font.baseSize; + + int i = 0, k = 0; + int glyphWidth = 0, prev = 1; + for (i = length; i >= 0; i--, k++) + { + glyphWidth = 0; + letter = GetPrevCodepoint(&text[i], &text[0], &prev); + + if (letter == 0x3f) prev = 1; + index = GetGlyphIndex(font, letter); + i -= prev - 1; + + if (letter != '\n') + { + glyphWidth = (font.chars[index].advanceX == 0)? + (int)(font.chars[index].rec.width*scaleFactor + spacing): + (int)(font.chars[index].advanceX*scaleFactor + spacing); + + if ((textOffsetX + glyphWidth + 1) >= rec.width) break; + } + else break; + + textOffsetX += glyphWidth; + } + + *pos = k; + + return (i + prev); +} + + +// Calculate cursor coordinates based on the cursor position `pos` inside the `text`. +static inline int GuiTextBoxGetCursorCoordinates(const char *text, int length, Rectangle rec, int pos) +{ + return GuiMeasureTextBox(text, length, rec, &pos, GUI_MEASURE_MODE_CURSOR_POS); +} + +// Calculate cursor position in textbox based on mouse coordinates. +static inline int GuiTextBoxGetCursorFromMouse(const char *text, int length, Rectangle rec, int* pos) +{ + return GuiMeasureTextBox(text, length, rec, pos, GUI_MEASURE_MODE_CURSOR_COORDS); +} + +// Calculates how many characters is the textbox able to draw inside rec +static inline int GuiTextBoxMaxCharacters(const char *text, int length, Rectangle rec) +{ + int pos = -1; + GuiMeasureTextBox(text, length, rec, &pos, GUI_MEASURE_MODE_CURSOR_END); + return pos; +} + +// Returns total number of characters(codepoints) in a UTF8 encoded `text` until `\0` or a `\n` is found. +// NOTE: If a invalid UTF8 sequence is encountered a `?`(0x3f) codepoint is counted instead. +static inline unsigned int GuiCountCodepointsUntilNewline(const char *text) +{ + unsigned int len = 0; + char *ptr = (char*)&text[0]; + + while ((*ptr != '\0') && (*ptr != '\n')) + { + int next = 0; + int letter = GetNextCodepoint(ptr, &next); + + if (letter == 0x3f) ptr += 1; + else ptr += next; + ++len; + } + + return len; +} + +static inline void MoveTextBoxCursorRight(const char* text, int length, Rectangle textRec) +{ + // FIXME: Counting codepoints each time we press the key is expensive, find another way + int count = GuiCountCodepointsUntilNewline(text); + if (guiTextBoxState.cursor < count ) guiTextBoxState.cursor++; + + const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); + + if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) + { + const int cidx = GuiTextBoxGetByteIndex(text, guiTextBoxState.index, guiTextBoxState.start, guiTextBoxState.cursor); + int pos = 0; + guiTextBoxState.index = GuiMeasureTextBoxRev(text, cidx - 1, textRec, &pos); + guiTextBoxState.start = guiTextBoxState.cursor - pos; + } +} + +static inline void MoveTextBoxCursorLeft(const char* text) +{ + if (guiTextBoxState.cursor > 0) guiTextBoxState.cursor--; + + if (guiTextBoxState.cursor < guiTextBoxState.start) + { + int prev = 0; + int letter = GetPrevCodepoint(&text[guiTextBoxState.index - 1], text, &prev); + if (letter == 0x3f) prev = 1; + guiTextBoxState.start--; + guiTextBoxState.index -= prev; + } +} + +RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to) +{ + int i = start, k = from; + + while ((text[i] != '\0') && (k < to)) + { + int j = 0; + int letter = GetNextCodepoint(&text[i], &j); + + if (letter == 0x3f) j = 1; + i += j; + ++k; + } + + return i; +} + +RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before) +{ + if ((guiTextBoxState.cursor != -1) && (text != NULL)) + { + int startIdx = 0, endIdx = 0; + if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) + { + // Delete selection + int start = guiTextBoxState.cursor; + int end = guiTextBoxState.select; + + if (guiTextBoxState.cursor > guiTextBoxState.select) + { + start = guiTextBoxState.select; + end = guiTextBoxState.cursor; + } + + // Convert to byte indexes + startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); + endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); + + // Adjust text box state + guiTextBoxState.cursor = start; // Always set cursor to start of selection + if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame + } + else + { + if (before) + { + // Delete character before cursor + if (guiTextBoxState.cursor != 0) + { + endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + guiTextBoxState.cursor--; + startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + + if (guiTextBoxState.cursor < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame + } + } + else + { + // Delete character after cursor + if (guiTextBoxState.cursor + 1 <= GuiCountCodepointsUntilNewline(text)) + { + startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor+1); + } + } + } + + memmove(&text[startIdx], &text[endIdx], length - endIdx); + text[length - (endIdx - startIdx)] = '\0'; + guiTextBoxState.select = -1; // Always deselect + + return (endIdx - startIdx); + } + + return 0; +} + +RAYGUIDEF void GuiTextBoxSelectAll(const char *text) +{ + guiTextBoxState.cursor = GuiCountCodepointsUntilNewline(text); + + if (guiTextBoxState.cursor > 0) + { + guiTextBoxState.select = 0; + guiTextBoxState.start = -1; // Force recalculate on the next frame + } + else guiTextBoxState.select = -1; +} + +RAYGUIDEF void GuiTextBoxCopy(const char *text) +{ + if ((text != NULL) && + (guiTextBoxState.select != -1) && + (guiTextBoxState.cursor != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) + { + int start = guiTextBoxState.cursor; + int end = guiTextBoxState.select; + + if (guiTextBoxState.cursor > guiTextBoxState.select) + { + start = guiTextBoxState.select; + end = guiTextBoxState.cursor; + } + + // Convert to byte indexes + start = GuiTextBoxGetByteIndex(text, 0, 0, start); + end = GuiTextBoxGetByteIndex(text, 0, 0, end); + + // FIXME: `TextSubtext()` only lets use copy MAX_TEXT_BUFFER_LENGTH (1024) bytes + // maybe modify `SetClipboardText()` so we can use it only on part of a string + const char *clipText = TextSubtext(text, start, end - start); + + SetClipboardText(clipText); + } +} + +// Paste text from clipboard into the active textbox. +// `text` is the pointer to the buffer used by the textbox while `textSize` is the text buffer max size +RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize) +{ + const char *clipText = GetClipboardText(); // GLFW guaratees this should be UTF8 encoded! + int length = strlen(text); + + if ((text != NULL) && (clipText != NULL) && (guiTextBoxState.cursor != -1)) + { + if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) + { + // If there's a selection we'll have to delete it first + length -= GuiTextBoxDelete(text, length, true); + } + + int clipLen = strlen(clipText); // We want the length in bytes + + // Calculate how many bytes can we copy from clipboard text before we run out of space + int size = ((length + clipLen) <= textSize) ? clipLen : textSize - length; + + // Make room by shifting to right the bytes after cursor + int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + int endIdx = startIdx + size; + memmove(&text[endIdx], &text[startIdx], length - startIdx); + text[length + size] = '\0'; // Set the NULL char + + // At long last copy the clipboard text + memcpy(&text[startIdx], clipText, size); + + // Set cursor position at the end of the pasted text + guiTextBoxState.cursor = 0; + + for (int i = 0; i < (startIdx + size); guiTextBoxState.cursor++) + { + int next = 0; + int letter = GetNextCodepoint(&text[i], &next); + if (letter != 0x3f) i += next; + else i += 1; + } + + guiTextBoxState.start = -1; // Force to recalculate on the next frame + } +} + +RAYGUIDEF void GuiTextBoxCut(char* text) +{ + if ((text != NULL) && + (guiTextBoxState.select != -1) && + (guiTextBoxState.cursor != -1) && + (guiTextBoxState.select != guiTextBoxState.cursor)) + { + // First copy selection to clipboard; + int start = guiTextBoxState.cursor, end = guiTextBoxState.select; + + if (guiTextBoxState.cursor > guiTextBoxState.select) + { + start = guiTextBoxState.select; + end = guiTextBoxState.cursor; + } + + // Convert to byte indexes + int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start); + int endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end); + + // FIXME: `TextSubtext()` only lets use copy MAX_TEXT_BUFFER_LENGTH (1024) bytes + // maybe modify `SetClipboardText()` so we can use it only on parts of a string + const char *clipText = TextSubtext(text, startIdx, endIdx - startIdx); + SetClipboardText(clipText); + + // Now delete selection (copy data over it) + int len = strlen(text); + memmove(&text[startIdx], &text[endIdx], len - endIdx); + text[len - (endIdx - startIdx)] = '\0'; + + // Adjust text box state + guiTextBoxState.cursor = start; // Always set cursor to start of selection + if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate + guiTextBoxState.select = -1; // Deselect + } +} + +static int EncodeCodepoint(unsigned int c, char out[5]) +{ + int len = 0; + if (c <= 0x7f) + { + out[0] = (char)c; + len = 1; + } + else if (c <= 0x7ff) + { + out[0] = (char)(((c >> 6) & 0x1f) | 0xc0); + out[1] = (char)((c & 0x3f) | 0x80); + len = 2; + } + else if (c <= 0xffff) + { + out[0] = (char)(((c >> 12) & 0x0f) | 0xe0); + out[1] = (char)(((c >> 6) & 0x3f) | 0x80); + out[2] = (char)((c & 0x3f) | 0x80); + len = 3; + } + else if (c <= 0x10ffff) + { + out[0] = (char)(((c >> 18) & 0x07) | 0xf0); + out[1] = (char)(((c >> 12) & 0x3f) | 0x80); + out[2] = (char)(((c >> 6) & 0x3f) | 0x80); + out[3] = (char)((c & 0x3f) | 0x80); + len = 4; + } + + out[len] = 0; + return len; +} + +// A text box control supporting text selection, cursor positioning and commonly used keyboard shortcuts. +// NOTE 1: Requires static variables: framesCounter +// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) +RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +{ + // Define the cursor movement/selection speed when movement keys are held/pressed + #define GUI_TEXTBOX_CURSOR_SPEED_MODIFIER 5 + + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + // Make sure length doesn't exceed `textSize`. `textSize` is actually the max amount of characters the textbox can handle. + int length = strlen(text); + if (length > textSize) + { + text[textSize] = '\0'; + length = textSize; + } + + // Make sure we have enough room to draw at least 1 character + if ((bounds.width - 2*GuiGetStyle(TEXTBOX, INNER_PADDING)) < GuiGetStyle(DEFAULT, TEXT_SIZE)) + { + bounds.width = GuiGetStyle(DEFAULT, TEXT_SIZE) + 2*GuiGetStyle(TEXTBOX, INNER_PADDING); + } + + // Center the text vertically + int verticalPadding = (bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) - GuiGetStyle(DEFAULT, TEXT_SIZE))/2; + + if (verticalPadding < 0) + { + // Make sure the height is sufficient + bounds.height = 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(DEFAULT, TEXT_SIZE); + verticalPadding = 0; + } + + // Calculate the drawing area for the text inside the control `bounds` + Rectangle textRec = { bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING), + bounds.y + verticalPadding + GuiGetStyle(TEXTBOX, BORDER_WIDTH), + bounds.width - 2*(GuiGetStyle(TEXTBOX, INNER_PADDING) + GuiGetStyle(TEXTBOX, BORDER_WIDTH)), + GuiGetStyle(DEFAULT, TEXT_SIZE) }; + + Vector2 cursorPos = { textRec.x, textRec.y }; // This holds the coordinates inside textRec of the cursor at current position and will be recalculated later + bool active = GuiTextBoxIsActive(bounds); // Check if this textbox is the global active textbox + + int selStart = 0, selLength = 0, textStartIndex = 0; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + const Vector2 mousePoint = GetMousePosition(); + + if (editMode) + { + // Check if we are the global active textbox + // A textbox becomes active when the user clicks it :) + if (!active) + { + if (CheckCollisionPointRec(mousePoint, bounds) && + (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonPressed(MOUSE_RIGHT_BUTTON))) + { + // Hurray!!! we just became the active textbox + active = true; + GuiTextBoxSetActive(bounds); + } + } + else if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) + { + // When active and the right mouse is clicked outside the textbox we should deactivate it + GuiTextBoxSetActive(RAYGUI_CLITERAL(Rectangle){0,0,-1,-1}); // Set a dummy rect as the active textbox bounds + active = false; + } + + if (active) + { + state = GUI_STATE_PRESSED; + framesCounter++; + + // Make sure state doesn't have invalid values + if (guiTextBoxState.cursor > length) guiTextBoxState.cursor = -1; + if (guiTextBoxState.select > length) guiTextBoxState.select = -1; + if (guiTextBoxState.start > length) guiTextBoxState.start = -1; + + + // Check textbox state for changes and recalculate if necesary + if (guiTextBoxState.cursor == -1) + { + // Set cursor to last visible character in textbox + guiTextBoxState.cursor = GuiTextBoxMaxCharacters(text, length, textRec); + } + + if (guiTextBoxState.start == -1) + { + // Force recalculate text start position and text start index + + // NOTE: start and index are always in sync + // start will hold the starting character position from where the text will be drawn + // while index will hold the byte index inside the text for that character + + if (guiTextBoxState.cursor == 0) + { + guiTextBoxState.start = guiTextBoxState.index = 0; // No need to recalculate + } + else + { + int pos = 0; + int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + guiTextBoxState.index = GuiMeasureTextBoxRev(text, len, textRec, &pos); + guiTextBoxState.start = guiTextBoxState.cursor - pos + 1; + } + } + + // ----------------- + // HANDLE KEY INPUT + // ----------------- + // * -> | LSHIFT + -> move cursor to the right | increase selection by one + // * <- | LSHIFT + <- move cursor to the left | decrease selection by one + // * HOME | LSHIFT + HOME moves cursor to start of text | selects text from cursor to start of text + // * END | LSHIFT + END move cursor to end of text | selects text from cursor until end of text + // * CTRL + A select all characters in text + // * CTRL + C copy selected text + // * CTRL + X cut selected text + // * CTRL + V remove selected text, if any, then paste clipboard data + // * DEL delete character or selection after cursor + // * BACKSPACE delete character or selection before cursor + // TODO: Add more shortcuts (insert mode, select word, moveto/select prev/next word ...) + if (IsKeyPressed(KEY_RIGHT) || + (IsKeyDown(KEY_RIGHT) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER == 0))) + { + if (IsKeyDown(KEY_LEFT_SHIFT)) + { + // Selecting + if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start + + MoveTextBoxCursorRight(text, length, textRec); + } + else + { + if (guiTextBoxState.select != -1 && guiTextBoxState.select != guiTextBoxState.cursor) + { + // Deselect and move cursor to end of selection + if (guiTextBoxState.cursor < guiTextBoxState.select) + { + guiTextBoxState.cursor = guiTextBoxState.select - 1; + MoveTextBoxCursorRight(text, length, textRec); + } + } + else + { + // Move cursor to the right + MoveTextBoxCursorRight(text, length, textRec); + } + + guiTextBoxState.select = -1; + } + + framesCounter = 0; + } + else if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER == 0))) + { + if (IsKeyDown(KEY_LEFT_SHIFT)) + { + // Selecting + if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start + + MoveTextBoxCursorLeft(text); + } + else + { + if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) + { + // Deselect and move cursor to start of selection + if (guiTextBoxState.cursor > guiTextBoxState.select) + { + guiTextBoxState.cursor = guiTextBoxState.select; + + if (guiTextBoxState.start > guiTextBoxState.cursor) + { + guiTextBoxState.start = guiTextBoxState.cursor; + guiTextBoxState.index = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.start); // Recalculate byte index + } + } + } + else + { + // Move cursor to the left + MoveTextBoxCursorLeft(text); + } + + guiTextBoxState.select = -1; + } + + framesCounter = 0; + } + else if (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0)) + { + GuiTextBoxDelete(text, length, true); + } + else if (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0)) + { + GuiTextBoxDelete(text, length, false); + } + else if (IsKeyPressed(KEY_HOME)) + { + if (IsKeyDown(KEY_LEFT_SHIFT)) + { + // Select from start of text to cursor + if ((guiTextBoxState.select > guiTextBoxState.cursor) || + ((guiTextBoxState.select == -1) && (guiTextBoxState.cursor != 0))) + { + guiTextBoxState.select = guiTextBoxState.cursor; + } + } + else guiTextBoxState.select = -1; // Deselect everything + + // Move cursor to start of text + guiTextBoxState.cursor = guiTextBoxState.start = guiTextBoxState.index = 0; + framesCounter = 0; + } + else if (IsKeyPressed(KEY_END)) + { + int max = GuiCountCodepointsUntilNewline(text); + + if (IsKeyDown(KEY_LEFT_SHIFT)) + { + if ((guiTextBoxState.select == -1) && (guiTextBoxState.cursor != max)) + { + guiTextBoxState.select = guiTextBoxState.cursor; + } + } + else guiTextBoxState.select = -1; // Deselect everything + + int pos = 0; + guiTextBoxState.cursor = max; + int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + guiTextBoxState.index = GuiMeasureTextBoxRev(text, len, textRec, &pos); + guiTextBoxState.start = guiTextBoxState.cursor - pos + 1; + } + else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_A)) + { + // `CTRL + A` Select all + GuiTextBoxSelectAll(text); + } + else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) + { + // `CTRL + C` Copy selected text to clipboard + GuiTextBoxCopy(text); + } + else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_X)) + { + // `CTRL + X` Cut selected text + GuiTextBoxCut(text); + } + else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V)) + { + // `CTRL + V` Paste clipboard text + GuiTextBoxPaste(text, textSize); + } + else if (IsKeyPressed(KEY_ENTER)) + { + pressed = true; + } + else + { + int key = GetKeyPressed(); + if ((key >= 32) && ((guiTextBoxState.cursor + 1) < textSize)) + { + if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor)) + { + // Delete selection + GuiTextBoxDelete(text, length, true); + } + + // Decode codepoint + char out[5] = {0}; + int sz = EncodeCodepoint(key, &out[0]); + + if (sz != 0) + { + int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor); + int endIdx = startIdx + sz; + + if (endIdx <= textSize && length < textSize - 1) + { + guiTextBoxState.cursor++; + guiTextBoxState.select = -1; + memmove(&text[endIdx], &text[startIdx], length - startIdx); + memcpy(&text[startIdx], &out[0], sz); + length += sz; + text[length] = '\0'; + + if (guiTextBoxState.start != -1) + { + const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); + + if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) guiTextBoxState.start = -1; + } + } + } + } + } + + // ------------- + // HANDLE MOUSE + // ------------- + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + if (CheckCollisionPointRec(mousePoint, textRec)) + { + GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, &guiTextBoxState.cursor); + guiTextBoxState.cursor += guiTextBoxState.start; + guiTextBoxState.select = -1; + } + else + { + // Clicked outside the `textRec` but still inside bounds + if (mousePoint.x <= bounds.x+bounds.width/2) guiTextBoxState.cursor = 0 + guiTextBoxState.start; + else guiTextBoxState.cursor = guiTextBoxState.start + GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); + guiTextBoxState.select = -1; + } + } + else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + int cursor = guiTextBoxState.cursor - guiTextBoxState.start; + bool move = false; + if (CheckCollisionPointRec(mousePoint, textRec)) + { + GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, &cursor); + } + else + { + // Clicked outside the `textRec` but still inside bounds, this means that we must move the text + move = true; + if (mousePoint.x > bounds.x+bounds.width/2) + { + cursor = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec); + } + } + + guiTextBoxState.cursor = cursor + guiTextBoxState.start; + + if (guiTextBoxState.select == -1) + { + // Mark start of selection + guiTextBoxState.select = guiTextBoxState.cursor; + } + + // Move the text when cursor is positioned before or after the text + if ((framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0 && move) + { + if (cursor == 0) MoveTextBoxCursorLeft(text); + else if (cursor == GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec)) + { + MoveTextBoxCursorRight(text, length, textRec); + } + } + } + } + + // Calculate X coordinate of the blinking cursor + cursorPos.x = GuiTextBoxGetCursorCoordinates(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, guiTextBoxState.cursor - guiTextBoxState.start); + + // Update variables + textStartIndex = guiTextBoxState.index; + + if (guiTextBoxState.select == -1) + { + selStart = guiTextBoxState.cursor; + selLength = 0; + } + else if (guiTextBoxState.cursor > guiTextBoxState.select) + { + selStart = guiTextBoxState.select; + selLength = guiTextBoxState.cursor - guiTextBoxState.select; + } + else + { + selStart = guiTextBoxState.cursor; + selLength = guiTextBoxState.select - guiTextBoxState.cursor; + } + + // We aren't drawing all of the text so make sure `DrawTextRecEx()` is selecting things correctly + if (guiTextBoxState.start > selStart) + { + selLength -= guiTextBoxState.start - selStart; + selStart = 0; + } + else selStart = selStart - guiTextBoxState.start; + } + else state = GUI_STATE_FOCUSED; + } + else + { + if (CheckCollisionPointRec(mousePoint, bounds)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(0)) pressed = true; } - else if (!CheckCollisionPointRec(mousePoint, openBounds)) + + if (active && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + // If active copy all text to clipboard even when disabled + + // Backup textbox state + int select = guiTextBoxState.select; + int cursor = guiTextBoxState.cursor; + int start = guiTextBoxState.start; + if (guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor) + { + // If no selection then mark all text to be copied to clipboard + GuiTextBoxSelectAll(text); + } + + GuiTextBoxCopy(text); + + // Restore textbox state + guiTextBoxState.select = select; + guiTextBoxState.cursor = cursor; + guiTextBoxState.start = start; } } } - //-------------------------------------------------------------------- - + // Draw control //-------------------------------------------------------------------- - - // TODO: Review this ugly hack... DROPDOWNBOX depends on GiListElement() that uses DEFAULT_TEXT_ALIGNMENT - int tempTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT)); - - switch (state) + DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); + + if (state == GUI_STATE_PRESSED) { - case GUI_STATE_NORMAL: - { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_NORMAL)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_NORMAL)), guiAlpha)); - - GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); - } break; - case GUI_STATE_FOCUSED: - { - GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, editMode); - } break; - case GUI_STATE_PRESSED: - { - if (!editMode) GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); - if (editMode) - { - GuiPanel(openBounds); - - GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); - - for (int i = 0; i < elementsCount; i++) - { - if (i == auxActive && editMode) - { - if (GuiListElement((Rectangle){ bounds.x, bounds.y + bounds.height*(i + 1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), - bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, - elementsPtrs[i], true, true) == false) pressed = true; - } - else - { - if (GuiListElement((Rectangle){ bounds.x, bounds.y + bounds.height*(i+1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), - bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, - elementsPtrs[i], false, true)) - { - auxActive = i; - pressed = true; - } - } - } - } - } break; - case GUI_STATE_DISABLED: + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); + if (editMode && active && ((framesCounter/TEXTEDIT_CURSOR_BLINK_FRAMES)%2 == 0) && selLength == 0) { - DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_DISABLED)), guiAlpha)); - DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_DISABLED)), guiAlpha)); - - GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); - } break; - default: break; + // Draw the blinking cursor + DrawRectangle(cursorPos.x, cursorPos.y, 1, GuiGetStyle(DEFAULT, TEXT_SIZE), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } } - - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, tempTextAlign); - - DrawTriangle((Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2 }, - (Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 5, bounds.y + bounds.height/2 - 2 + 5 }, - (Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 10, bounds.y + bounds.height/2 - 2 }, - Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); - //-------------------------------------------------------------------- - - *active = auxActive; + else if (state == GUI_STATE_DISABLED) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + } + + // Finally draw the text and selection + DrawTextRecEx(guiFont, &text[textStartIndex], textRec, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), false, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha), selStart, selLength, GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_FG)), GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_BG))); + return pressed; } +#else // !RAYGUI_TEXTBOX_EXTENDED // Spinner control, returns selected value // NOTE: Requires static variables: framesCounter, valueSpeed - ERROR! @@ -1401,10 +2512,10 @@ RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxVal bool pressed = false; int tempValue = *value; - Rectangle spinner = { bounds.x + GuiGetStyle(TEXTBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(TEXTBOX, SPINNER_BUTTON_PADDING), bounds.y, - bounds.width - 2*(GuiGetStyle(TEXTBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(TEXTBOX, SPINNER_BUTTON_PADDING)), bounds.height }; - Rectangle leftButtonBound = { bounds.x, bounds.y, GuiGetStyle(TEXTBOX, SPINNER_BUTTON_WIDTH), bounds.height }; - Rectangle rightButtonBound = { bounds.x + bounds.width - GuiGetStyle(TEXTBOX, SPINNER_BUTTON_WIDTH), bounds.y, GuiGetStyle(TEXTBOX, SPINNER_BUTTON_WIDTH), bounds.height }; + Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING), bounds.y, + bounds.width - 2*(GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING)), bounds.height }; + Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.height }; + Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.height }; // Update control //-------------------------------------------------------------------- @@ -1417,12 +2528,13 @@ RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxVal // Draw control //-------------------------------------------------------------------- + // TODO: Set Spinner properties for ValueBox pressed = GuiValueBox(spinner, &tempValue, minValue, maxValue, editMode); // Draw value selector custom buttons // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); - GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(TEXTBOX, SPINNER_BUTTON_BORDER_WIDTH)); + GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH)); int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); @@ -1476,7 +2588,7 @@ RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxVa // Only allow keys in range [48..57] if (keyCount < VALUEBOX_MAX_CHARS) { - int maxWidth = (bounds.width - (GuiGetStyle(DEFAULT, INNER_PADDING)*2)); + int maxWidth = (bounds.width - (GuiGetStyle(VALUEBOX, INNER_PADDING)*2)); if (GetTextWidth(text) < maxWidth) { int key = GetKeyPressed(); @@ -1536,19 +2648,20 @@ RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxVa // Draw control //-------------------------------------------------------------------- - DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha)); if (state == GUI_STATE_PRESSED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); - if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GetTextWidth(text)/2 + bounds.width/2 + 2, bounds.y + GuiGetStyle(TEXTBOX, INNER_PADDING), 1, bounds.height - GuiGetStyle(TEXTBOX, INNER_PADDING)*2, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_FOCUSED)), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)), guiAlpha)); + + if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GetTextWidth(text)/2 + bounds.width/2 + 2, bounds.y + GuiGetStyle(VALUEBOX, INNER_PADDING), 1, bounds.height - GuiGetStyle(VALUEBOX, INNER_PADDING)*2, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); } else if (state == GUI_STATE_DISABLED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)), guiAlpha)); } - GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, GetTextBounds(VALUEBOX, bounds), GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- return pressed; @@ -1590,6 +2703,7 @@ RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editM { text[keyCount] = (char)key; keyCount++; + text[keyCount] = '\0'; } } } @@ -1636,11 +2750,10 @@ RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editM if (state == GUI_STATE_PRESSED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); - + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + // Draw blinking cursor - // TODO: Consider TEXTBOX TEXT_ALIGNMENT - if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, INNER_PADDING) + GetTextWidth(text) + 2, bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), 1, GuiGetStyle(DEFAULT, TEXT_SIZE)*2, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, INNER_PADDING) + GetTextWidth(text) + 2 + bounds.width/2*GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), 1, GuiGetStyle(DEFAULT, TEXT_SIZE)*2, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); } else if (state == GUI_STATE_DISABLED) { @@ -1652,6 +2765,7 @@ RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editM return pressed; } +#endif // Text Box control with multiple lines RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) @@ -1665,9 +2779,6 @@ RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool int currentLine = 0; //const char *numChars = NULL; - // Security check because font is used directly in this control - if (guiFont.texture.id == 0) guiFont = GetFontDefault(); - // Update control //-------------------------------------------------------------------- if ((state != GUI_STATE_DISABLED) && !guiLocked) @@ -1825,7 +2936,7 @@ RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool if (state == GUI_STATE_PRESSED) { - DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); if (editMode) { @@ -1842,7 +2953,7 @@ RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool } // Draw characters counter - //GuiDrawText(numChars, (Vector2){ bounds.x + bounds.width - GetTextWidth(numChars) - GuiGetStyle(TEXTBOX, INNER_PADDING), bounds.y + bounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE) - GuiGetStyle(TEXTBOX, INNER_PADDING) }, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT_COLOR_PRESSED)), guiAlpha/2)); + //GuiDrawText(numChars, RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GetTextWidth(numChars) - GuiGetStyle(TEXTBOX, INNER_PADDING), bounds.y + bounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE) - GuiGetStyle(TEXTBOX, INNER_PADDING) }, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT_COLOR_PRESSED)), guiAlpha/2)); } } else if (state == GUI_STATE_DISABLED) @@ -1908,7 +3019,7 @@ RAYGUIDEF float GuiSliderPro(Rectangle bounds, const char *text, float value, fl if (value > maxValue) value = maxValue; else if (value < minValue) value = minValue; } - + // Bar limits check if (sliderWidth > 0) // Slider { @@ -1925,14 +3036,17 @@ RAYGUIDEF float GuiSliderPro(Rectangle bounds, const char *text, float value, fl //-------------------------------------------------------------------- DrawRectangleLinesEx(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha)); DrawRectangle(bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, (state == GUI_STATE_NORMAL)? BASE_COLOR_PRESSED : (BASE + (state*3)))), guiAlpha)); + + // Draw slider internal bar (depends on state) + if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == GUI_STATE_FOCUSED) DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); GuiDrawText(text, textBounds, GuiGetStyle(SLIDER, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); // TODO: Review showValue parameter, really ugly... - if (showValue) GuiDrawText(TextFormat("%.02f", value), (Rectangle){ bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING), - bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING), - GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SIZE) }, GUI_TEXT_ALIGN_LEFT, + if (showValue) GuiDrawText(TextFormat("%.02f", value), RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING), + (float)bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING), + (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- @@ -1967,11 +3081,13 @@ RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, // Draw control //-------------------------------------------------------------------- - if (showValue) GuiLabel((Rectangle){ bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING), bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING), GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SIZE) }, TextFormat("%.02f", value)); + if (showValue) GuiLabel(RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING), (float)bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING), (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, TextFormat("%.02f", value)); - DrawRectangleLinesEx(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, (state != GUI_STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha)); - DrawRectangle(bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); - DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha)); + + // Draw slider internal progress bar (depends on state) + if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha)); + else if (state == GUI_STATE_FOCUSED) DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha)); //-------------------------------------------------------------------- return value; @@ -1985,7 +3101,7 @@ RAYGUIDEF void GuiStatusBar(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha)); - DrawRectangleRec((Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 }, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 }, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); //-------------------------------------------------------------------- @@ -2028,14 +3144,17 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal bool isVertical = (bounds.width > bounds.height)? false : true; // The size (width or height depending on scrollbar type) of the spinner buttons - const int spinnerSize = GuiGetStyle(SCROLLBAR, SHOW_SPINNER_BUTTONS)? (isVertical? bounds.width - 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : bounds.height - 2 * GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0; + const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)? (isVertical? bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0; - // Spinner buttons [<] [>] [∧] [∨] - Rectangle spinnerUpLeft, spinnerDownRight; - // Actual area of the scrollbar excluding the spinner buttons - Rectangle scrollbar; // ------------ + // Arrow buttons [<] [>] [∧] [∨] + Rectangle arrowUpLeft = { 0 }; + Rectangle arrowDownRight = { 0 }; + + // Actual area of the scrollbar excluding the arrow buttons + Rectangle scrollbar = { 0 }; + // Slider bar that moves --[///]----- - Rectangle slider; + Rectangle slider = { 0 }; // Normalize value if (value > maxValue) value = maxValue; @@ -2043,23 +3162,23 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal const int range = maxValue - minValue; int sliderSize = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); - + // Calculate rectangles for all of the components - spinnerUpLeft = (Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), spinnerSize, spinnerSize }; + arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; if (isVertical) { - spinnerDownRight = (Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), spinnerSize, spinnerSize}; - scrollbar = (Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING), spinnerUpLeft.y + spinnerUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING)), bounds.height - spinnerUpLeft.height - spinnerDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize}; + scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; sliderSize = (sliderSize >= scrollbar.height)? (scrollbar.height - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = (Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING),scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)),bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)), sliderSize }; + slider = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING), (float)scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)), (float)bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)), (float)sliderSize }; } else { - spinnerDownRight = (Rectangle){ bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), spinnerSize, spinnerSize}; - scrollbar = (Rectangle){ spinnerUpLeft.x + spinnerUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING), bounds.width - spinnerUpLeft.width - spinnerDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING))}; + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize}; + scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING))}; sliderSize = (sliderSize >= scrollbar.width)? (scrollbar.width - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = (Rectangle){ scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING), sliderSize, bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)) }; + slider = RAYGUI_CLITERAL(Rectangle){ (float)scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING), (float)sliderSize, (float)bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)) }; } // Update control @@ -2078,8 +3197,8 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - if (CheckCollisionPointRec(mousePoint, spinnerUpLeft)) value -= range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - else if (CheckCollisionPointRec(mousePoint, spinnerDownRight)) value += range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); state = GUI_STATE_PRESSED; } @@ -2087,12 +3206,12 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal { if (!isVertical) { - Rectangle scrollArea = { spinnerUpLeft.x + spinnerUpLeft.width, spinnerUpLeft.y, scrollbar.width, bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)}; + Rectangle scrollArea = { arrowUpLeft.x + arrowUpLeft.width, arrowUpLeft.y, scrollbar.width, bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)}; if (CheckCollisionPointRec(mousePoint, scrollArea)) value = ((float)(mousePoint.x - scrollArea.x - slider.width/2)*range)/(scrollArea.width - slider.width) + minValue; } else { - Rectangle scrollArea = { spinnerUpLeft.x, spinnerUpLeft.y+spinnerUpLeft.height, bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), scrollbar.height}; + Rectangle scrollArea = { arrowUpLeft.x, arrowUpLeft.y+arrowUpLeft.height, bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), scrollbar.height}; if (CheckCollisionPointRec(mousePoint, scrollArea)) value = ((float)(mousePoint.y - scrollArea.y - slider.height/2)*range)/(scrollArea.height - slider.height) + minValue; } } @@ -2104,7 +3223,6 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal } //-------------------------------------------------------------------- - // Draw control //-------------------------------------------------------------------- DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background @@ -2114,54 +3232,44 @@ RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxVal DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar - // Draw arrows using lines + // Draw arrows const int padding = (spinnerSize - GuiGetStyle(SCROLLBAR, ARROWS_SIZE))/2; const Vector2 lineCoords[] = { - //coordinates for < 0,1,2 - {spinnerUpLeft.x + padding, spinnerUpLeft.y + spinnerSize/2}, - {spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + padding }, - {spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + spinnerSize - padding}, - - //coordinates for > 3,4,5 - {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, - {spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + spinnerSize/2 }, - {spinnerDownRight.x + padding, spinnerDownRight.y + spinnerSize - padding}, - - //coordinates for ∧ 6,7,8 - {spinnerUpLeft.x + spinnerSize/2, spinnerUpLeft.y + padding}, - {spinnerUpLeft.x + padding, spinnerUpLeft.y + spinnerSize - padding}, - {spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + spinnerSize - padding}, - - //coordinates for ∨ 9,10,11 - {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, - {spinnerDownRight.x + spinnerSize/2, spinnerDownRight.y + spinnerSize - padding }, - {spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + padding} + // Coordinates for < 0,1,2 + { arrowUpLeft.x + padding, arrowUpLeft.y + spinnerSize/2 }, + { arrowUpLeft.x + spinnerSize - padding, arrowUpLeft.y + padding }, + { arrowUpLeft.x + spinnerSize - padding, arrowUpLeft.y + spinnerSize - padding }, + + // Coordinates for > 3,4,5 + { arrowDownRight.x + padding, arrowDownRight.y + padding }, + { arrowDownRight.x + spinnerSize - padding, arrowDownRight.y + spinnerSize/2 }, + { arrowDownRight.x + padding, arrowDownRight.y + spinnerSize - padding }, + + // Coordinates for ∧ 6,7,8 + { arrowUpLeft.x + spinnerSize/2, arrowUpLeft.y + padding }, + { arrowUpLeft.x + padding, arrowUpLeft.y + spinnerSize - padding }, + { arrowUpLeft.x + spinnerSize - padding, arrowUpLeft.y + spinnerSize - padding }, + + // Coordinates for ∨ 9,10,11 + { arrowDownRight.x + padding, arrowDownRight.y + padding }, + { arrowDownRight.x + spinnerSize/2, arrowDownRight.y + spinnerSize - padding }, + { arrowDownRight.x + spinnerSize - padding, arrowDownRight.y + padding } }; Color lineColor = Fade(GetColor(GuiGetStyle(BUTTON, TEXT + state*3)), guiAlpha); - if (GuiGetStyle(SCROLLBAR, SHOW_SPINNER_BUTTONS)) + if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) { if (isVertical) { - // Draw ∧ - DrawLineEx(lineCoords[6], lineCoords[7], 3.0f, lineColor); - DrawLineEx(lineCoords[6], lineCoords[8], 3.0f, lineColor); - - // Draw ∨ - DrawLineEx(lineCoords[9], lineCoords[10], 3.0f, lineColor); - DrawLineEx(lineCoords[11], lineCoords[10], 3.0f, lineColor); + DrawTriangle(lineCoords[6], lineCoords[7], lineCoords[8], lineColor); + DrawTriangle(lineCoords[9], lineCoords[10], lineCoords[11], lineColor); } else { - // Draw < - DrawLineEx(lineCoords[0], lineCoords[1], 3.0f, lineColor); - DrawLineEx(lineCoords[0], lineCoords[2], 3.0f, lineColor); - - // Draw > - DrawLineEx(lineCoords[3], lineCoords[4], 3.0f, lineColor); - DrawLineEx(lineCoords[5], lineCoords[4], 3.0f, lineColor); + DrawTriangle(lineCoords[2], lineCoords[1], lineCoords[0], lineColor); + DrawTriangle(lineCoords[5], lineCoords[4], lineCoords[3], lineColor); } } //-------------------------------------------------------------------- @@ -2294,12 +3402,12 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int elementWidth = bounds.width - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); } - Rectangle scrollBarRect = { bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; + Rectangle scrollBarRect = { (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), (float)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; if (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_RIGHT_SIDE) scrollBarRect.x = posX + elementWidth + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING); // Area without the scrollbar - Rectangle viewArea = { posX, bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), elementWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; + Rectangle viewArea = { (float)posX, (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)elementWidth, (float)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; if ((state != GUI_STATE_DISABLED) && !guiLocked) // && !guiLocked { @@ -2389,7 +3497,7 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int // Get focused element for (int i = startIndex; i < endIndex; i++) { - if (CheckCollisionPointRec(mousePoint, (Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) })) + if (CheckCollisionPointRec(mousePoint, RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) })) { focusElement = i; } @@ -2397,7 +3505,7 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int } const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); // Save default slider size - + // Calculate percentage of visible elements and apply same percentage to scrollbar if (useScrollBar) { @@ -2455,16 +3563,16 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int if ((enabled != NULL) && (enabled[i] == 0)) { GuiDisable(); - GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); + GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); GuiEnable(); } else if (i == auxActive) { GuiDisable(); - GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); + GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); GuiEnable(); } - else GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); + else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); } } break; case GUI_STATE_FOCUSED: @@ -2474,11 +3582,11 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int if ((enabled != NULL) && (enabled[i] == 0)) { GuiDisable(); - GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); + GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); GuiEnable(); } - else if (i == auxActive) GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); - else GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); + else if (i == auxActive) GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); + else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); } } break; case GUI_STATE_PRESSED: @@ -2488,16 +3596,16 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int if ((enabled != NULL) && (enabled[i] == 0)) { GuiDisable(); - GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); + GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); GuiEnable(); } else if ((i == auxActive) && editMode) { - if (GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, true) == false) auxActive = -1; + if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, true) == false) auxActive = -1; } else { - if (GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, true) == true) auxActive = i; + if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, true) == true) auxActive = i; } } } break; @@ -2505,8 +3613,8 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int { for (int i = startIndex; i < endIndex; i++) { - if (i == auxActive) GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); - else GuiListElement((Rectangle){ posX, bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), elementWidth, GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); + if (i == auxActive) GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false); + else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false); } } break; default: break; @@ -2521,7 +3629,7 @@ RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int } // Color Panel control -RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color) +RAYGUIDEF Color GuiColorPanelEx(Rectangle bounds, Color color, float hue) { GuiControlState state = guiState; Vector2 pickerSelector = { 0 }; @@ -2532,11 +3640,14 @@ RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color) pickerSelector.x = bounds.x + (float)hsv.y*bounds.width; // HSV: Saturation pickerSelector.y = bounds.y + (1.0f - (float)hsv.z)*bounds.height; // HSV: Value - Vector3 maxHue = { hsv.x, 1.0f, 1.0f }; + Vector3 maxHue = { hue >= 0.0f ? hue : hsv.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 }; + + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; // Update control //-------------------------------------------------------------------- @@ -2563,7 +3674,7 @@ RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color) Vector3 rgb = ConvertHSVtoRGB(hsv); // NOTE: Vector3ToColor() only available on raylib 1.8.1 - color = (Color){ (unsigned char)(255.0f*rgb.x), + color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), (unsigned char)(255.0f*rgb.y), (unsigned char)(255.0f*rgb.z), (unsigned char)(255.0f*(float)color.a/255.0f) }; @@ -2578,23 +3689,28 @@ RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color) //-------------------------------------------------------------------- if (state != GUI_STATE_DISABLED) { - DrawRectangleGradientEx(bounds, Fade(WHITE, guiAlpha), Fade(WHITE, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha)); - DrawRectangleGradientEx(bounds, Fade(BLACK, 0), Fade(BLACK, guiAlpha), Fade(BLACK, guiAlpha), Fade(BLACK, 0)); + 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 - DrawRectangle(pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), Fade(WHITE, guiAlpha)); + DrawRectangle(pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), Fade(colWhite, guiAlpha)); } else { - DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(BLACK, 0.6f), guiAlpha), Fade(Fade(BLACK, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); + 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)); } - DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- return color; } +RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color) +{ + return GuiColorPanelEx(bounds, color, -1.0f); +} + // Color Bar Alpha control // NOTE: Returns alpha value normalized [0..1] RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) @@ -2602,7 +3718,7 @@ RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) #define COLORBARALPHA_CHECKED_SIZE 10 GuiControlState state = guiState; - Rectangle selector = { bounds.x + alpha*bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), bounds.y - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT), bounds.height + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2 }; + Rectangle selector = { (float)bounds.x + alpha*bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.y - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2 }; // Update control //-------------------------------------------------------------------- @@ -2630,25 +3746,33 @@ RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) // Draw control //-------------------------------------------------------------------- + // Draw alpha bar: checked background if (state != GUI_STATE_DISABLED) { - for (int i = 0; i < bounds.width/COLORBARALPHA_CHECKED_SIZE; i++) DrawRectangle(bounds.x + COLORBARALPHA_CHECKED_SIZE*(i%((int)bounds.width/COLORBARALPHA_CHECKED_SIZE)), bounds.y, bounds.width/(bounds.width/COLORBARALPHA_CHECKED_SIZE), COLORBARALPHA_CHECKED_SIZE, (i%2)? Fade(Fade(GRAY, 0.4f), guiAlpha) : Fade(Fade(RAYWHITE, 0.4f), guiAlpha)); - for (int i = 0; i < bounds.width/COLORBARALPHA_CHECKED_SIZE; i++) DrawRectangle(bounds.x + COLORBARALPHA_CHECKED_SIZE*(i%((int)bounds.width/COLORBARALPHA_CHECKED_SIZE)), bounds.y + COLORBARALPHA_CHECKED_SIZE, bounds.width/(bounds.width/COLORBARALPHA_CHECKED_SIZE), COLORBARALPHA_CHECKED_SIZE, (i%2)? Fade(Fade(RAYWHITE, 0.4f), guiAlpha) : Fade(Fade(GRAY, 0.4f), guiAlpha)); - DrawRectangleGradientH(bounds.x, bounds.y, bounds.width, bounds.height, Fade((Color){ 255,255,255,0 }, guiAlpha), Fade((Color){ 0,0,0,255 }, guiAlpha)); - } - else DrawRectangleGradientH(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); - - DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + int checksX = bounds.width/COLORBARALPHA_CHECKED_SIZE; + int checksY = bounds.height/COLORBARALPHA_CHECKED_SIZE; + + for (int x = 0; x < checksX; x++) + { + for (int y = 0; y < checksY; y++) + { + DrawRectangle(bounds.x + x*COLORBARALPHA_CHECKED_SIZE, + bounds.y + y*COLORBARALPHA_CHECKED_SIZE, + COLORBARALPHA_CHECKED_SIZE, COLORBARALPHA_CHECKED_SIZE, + ((x + y)%2)? Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) : + Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha)); + } + } - switch (state) - { - case GUI_STATE_NORMAL: DrawRectangle(selector.x , selector.y, selector.width, selector.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_PRESSED)), guiAlpha)); break; - case GUI_STATE_FOCUSED: DrawRectangle(selector.x, selector.y, selector.width, selector.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_FOCUSED)), guiAlpha)); break; - case GUI_STATE_PRESSED: DrawRectangle(selector.x, selector.y, selector.width, selector.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_PRESSED)), guiAlpha)); break; - case GUI_STATE_DISABLED: DrawRectangleRec(selector, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); break; - default: break; + DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha)); } + else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + + // Draw alpha bar: selector + DrawRectangleRec(selector, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- return alpha; @@ -2659,7 +3783,7 @@ RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha) RAYGUIDEF float GuiColorBarHue(Rectangle bounds, float hue) { GuiControlState state = guiState; - Rectangle selector = { bounds.x - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), bounds.y + hue/360.0f*bounds.height - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), bounds.width + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2, GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT) }; + Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.y + hue/360.0f*bounds.height - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.width + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2, (float)GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT) }; // Update control //-------------------------------------------------------------------- @@ -2701,21 +3825,19 @@ RAYGUIDEF float GuiColorBarHue(Rectangle bounds, float hue) if (state != GUI_STATE_DISABLED) { // Draw hue bar:color bars - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade((Color){ 255,0,0,255 }, guiAlpha), Fade((Color){ 255,255,0,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + (int)bounds.height/6 + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade((Color){ 255,255,0,255 }, guiAlpha), Fade((Color){ 0,255,0,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 2*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade((Color){ 0,255,0,255 }, guiAlpha), Fade((Color){ 0,255,255,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 3*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade((Color){ 0,255,255,255 }, guiAlpha), Fade((Color){ 0,0,255,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 4*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade((Color){ 0,0,255,255 }, guiAlpha), Fade((Color){ 255,0,255,255 }, guiAlpha)); - DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 5*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6 - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), Fade((Color){ 255,0,255,255 }, guiAlpha), Fade((Color){ 255,0,0,255 }, guiAlpha)); - } - else - { - DrawRectangleGradientV(bounds.x, bounds.y, bounds.width, bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + (int)bounds.height/6 + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 2*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 3*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 4*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha)); + DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 5*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6 - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha)); } + else DrawRectangleGradientV(bounds.x, bounds.y, bounds.width, bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); // Draw hue bar: selector - DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); - DrawRectangle(selector.x, selector.y, selector.width, selector.height, Fade(GetColor(GuiGetStyle(COLORPICKER, (state == GUI_STATE_NORMAL)? BORDER_COLOR_PRESSED : (BORDER + state*3))), guiAlpha)); + DrawRectangleRec(selector, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- return hue; @@ -2735,14 +3857,14 @@ RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color) { color = GuiColorPanel(bounds, color); - Rectangle boundsHue = { bounds.x + bounds.width + GuiGetStyle(COLORPICKER, BAR_PADDING), bounds.y, GuiGetStyle(COLORPICKER, BAR_WIDTH), bounds.height }; + Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, BAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, BAR_WIDTH), (float)bounds.height }; //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) }; - Vector3 hsv = ConvertRGBtoHSV((Vector3){ color.r/255.0f, color.g/255.0f, color.b/255.0f }); + Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ color.r/255.0f, color.g/255.0f, color.b/255.0f }); hsv.x = GuiColorBarHue(boundsHue, hsv.x); //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f); Vector3 rgb = ConvertHSVtoRGB(hsv); - color = (Color){ (unsigned char)(rgb.x*255.0f), (unsigned char)(rgb.y*255.0f), (unsigned char)(rgb.z*255.0f), color.a }; + color = RAYGUI_CLITERAL(Color){ (unsigned char)(rgb.x*255.0f), (unsigned char)(rgb.y*255.0f), (unsigned char)(rgb.z*255.0f), color.a }; return color; } @@ -2754,12 +3876,12 @@ RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const cha #define MESSAGEBOX_BUTTON_PADDING 10 int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button - + int buttonsCount = 0; const char **buttonsText = GuiTextSplit(buttons, &buttonsCount, NULL); Vector2 textSize = MeasureTextEx(guiFont, message, GuiGetStyle(DEFAULT, TEXT_SIZE), 1); - + Rectangle textBounds = { 0 }; textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + (bounds.height - WINDOW_STATUSBAR_HEIGHT)/4 - textSize.y/2; @@ -2775,7 +3897,7 @@ RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const cha // Draw control //-------------------------------------------------------------------- if (GuiWindowBox(bounds, windowTitle)) clicked = 0; - + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); GuiLabel(textBounds, message); @@ -2783,19 +3905,29 @@ RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const cha prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); - + for (int i = 0; i < buttonsCount; i++) { if (GuiButton(buttonBounds, buttonsText[i])) clicked = i + 1; buttonBounds.x += (buttonBounds.width + MESSAGEBOX_BUTTON_PADDING); } - + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment); //-------------------------------------------------------------------- return clicked; } +// Text Input Box control, ask for text +RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *windowTitle, const char *message, char *text, const char *buttons) +{ + int btnIndex = -1; + + // TODO: GuiTextInputBox() + + return btnIndex; +} + // Grid control // NOTE: Returns grid mouse-hover selected cell // About drawing lines at subpixel spacing, simple put, not easy solution: @@ -2832,13 +3964,13 @@ RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) // Draw vertical grid lines for (int i = 0; i < linesV; i++) { - DrawRectangleRec((Rectangle){ bounds.x + spacing*i, bounds.y, 1, bounds.height }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); + DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + spacing*i, bounds.y, 1, bounds.height }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); } // Draw horizontal grid lines for (int i = 0; i < linesH; i++) { - DrawRectangleRec((Rectangle){ bounds.x, bounds.y + spacing*i, bounds.width, 1 }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); + DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + spacing*i, bounds.width, 1 }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA)); } } break; @@ -2855,45 +3987,117 @@ RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) // Load raygui style file (.rgs) RAYGUIDEF void GuiLoadStyle(const char *fileName) { - FILE *rgsFile = fopen(fileName, "rb"); + bool tryBinary = false; + + // Try reading the files as text file first + FILE *rgsFile = fopen(fileName, "rt"); if (rgsFile != NULL) { - unsigned int value = 0; + char buffer[256] = { 0 }; + fgets(buffer, 256, rgsFile); + + if (buffer[0] == '#') + { + int controlId = 0; + int propertyId = 0; + int propertyValue = 0; + + while (!feof(rgsFile)) + { + switch (buffer[0]) + { + case 'p': + { + sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue); + + if (controlId == 0) // DEFAULT control + { + // If a DEFAULT property is loaded, it is propagated to all controls, + // NOTE: All DEFAULT properties should be defined first in the file + GuiSetStyle(0, propertyId, propertyValue); + + if (propertyId < NUM_PROPS_DEFAULT) for (int i = 1; i < NUM_CONTROLS; i++) GuiSetStyle(i, propertyId, propertyValue); + } + else GuiSetStyle(controlId, propertyId, propertyValue); + + } break; + case 'f': + { + int fontSize = 0; + int fontSpacing = 0; + char fontFileName[256] = { 0 }; + sscanf(buffer, "f %d %d %[^\n]s", &fontSize, &fontSpacing, fontFileName); + + Font font = LoadFontEx(FormatText("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); + + if ((font.texture.id > 0) && (font.charsCount > 0)) + { + GuiFont(font); + GuiSetStyle(DEFAULT, TEXT_SIZE, fontSize); + GuiSetStyle(DEFAULT, TEXT_SPACING, fontSpacing); + } + } break; + default: break; + } + + fgets(buffer, 256, rgsFile); + } + } + else tryBinary = true; + + fclose(rgsFile); + } + else return; + + if (tryBinary) + { + rgsFile = fopen(fileName, "rb"); + + if (rgsFile == NULL) return; char signature[5] = ""; short version = 0; - short numControls = 0; - short numPropsDefault = 0; - short numPropsExtended = 0; + short reserved = 0; + int propertiesCount = 0; fread(signature, 1, 4, rgsFile); fread(&version, 1, sizeof(short), rgsFile); - fread(&numControls, 1, sizeof(short), rgsFile); - fread(&numPropsDefault, 1, sizeof(short), rgsFile); - fread(&numPropsExtended, 1, sizeof(short), rgsFile); + fread(&reserved, 1, sizeof(short), rgsFile); + fread(&propertiesCount, 1, sizeof(int), rgsFile); if ((signature[0] == 'r') && (signature[1] == 'G') && (signature[2] == 'S') && (signature[3] == ' ')) { - for (int i = 0; i < NUM_CONTROLS; i++) + short controlId = 0; + short propertyId = 0; + int propertyValue = 0; + + for (int i = 0; i < propertiesCount; i++) { - for (int j = 0; j < NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED; j++) + fread(&controlId, 1, sizeof(short), rgsFile); + fread(&propertyId, 1, sizeof(short), rgsFile); + fread(&propertyValue, 1, sizeof(int), rgsFile); + + if (controlId == 0) // DEFAULT control { - fread(&value, 1, sizeof(unsigned int), rgsFile); - GuiSetStyle(i, j, value); + // If a DEFAULT property is loaded, it is propagated to all controls + // NOTE: All DEFAULT properties should be defined first in the file + GuiSetStyle(0, (int)propertyId, propertyValue); + + if (propertyId < NUM_PROPS_DEFAULT) for (int i = 1; i < NUM_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue); } + else GuiSetStyle((int)controlId, (int)propertyId, propertyValue); } // Font loading is highly dependant on raylib API to load font data and image // TODO: Find some mechanism to support it in standalone mode - #if !defined(RAYGUI_STANDALONE) // Load custom font if available int fontDataSize = 0; - fwrite(&fontDataSize, 1, sizeof(int), rgsFile); + fread(&fontDataSize, 1, sizeof(int), rgsFile); if (fontDataSize > 0) { @@ -2919,9 +4123,12 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) fread(&imFont.width, 1, sizeof(int), rgsFile); fread(&imFont.height, 1, sizeof(int), rgsFile); fread(&imFont.format, 1, sizeof(int), rgsFile); - fread(&imFont.data, 1, fontImageSize, rgsFile); + + imFont.data = (unsigned char *)malloc(fontImageSize); + fread(imFont.data, 1, fontImageSize, rgsFile); font.texture = LoadTextureFromImage(imFont); + UnloadImage(imFont); } @@ -2944,7 +4151,7 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) } #endif } - + fclose(rgsFile); } } @@ -2958,7 +4165,7 @@ RAYGUIDEF void GuiLoadStyleProps(const int *props, int count) // Load style palette values from array (complete property sets) for (int i = 0; i < completeSets; i++) { - for (int j = 0; j < (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); i++) GuiSetStyle(i, j, props[i]); + for (int j = 0; j < (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); j++) GuiSetStyle(i, j, props[i]); } // Load style palette values from array (uncomplete property set) @@ -2994,6 +4201,8 @@ RAYGUIDEF void GuiLoadStyleDefault(void) { for (int j = 0; j < NUM_PROPS_DEFAULT; j++) GuiSetStyle(i, j, GuiGetStyle(DEFAULT, j)); } + + guiFont = GetFontDefault(); // Initialize default font // Initialize extended property values // NOTE: By default, extended property values are initialized to 0 @@ -3015,26 +4224,28 @@ RAYGUIDEF void GuiLoadStyleDefault(void) GuiSetStyle(TEXTBOX, INNER_PADDING, 4); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); GuiSetStyle(TEXTBOX, MULTILINE_PADDING, 5); - GuiSetStyle(TEXTBOX, SPINNER_BUTTON_WIDTH, 20); // SPINNER specific property - GuiSetStyle(TEXTBOX, SPINNER_BUTTON_PADDING, 2); // SPINNER specific property - GuiSetStyle(TEXTBOX, SPINNER_BUTTON_BORDER_WIDTH, 1); // SPINNER specific property - //GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); // TODO. - GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 6); - GuiSetStyle(COLORPICKER, BAR_WIDTH, 0x14); - GuiSetStyle(COLORPICKER, BAR_PADDING, 0xa); - GuiSetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT, 6); - GuiSetStyle(COLORPICKER, BAR_SELECTOR_PADDING, 2); - GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, 0x1e); - GuiSetStyle(LISTVIEW, ELEMENTS_PADDING, 2); - GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 10); - GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); + GuiSetStyle(TEXTBOX, COLOR_SELECTED_FG, 0xf0fffeff); + GuiSetStyle(TEXTBOX, COLOR_SELECTED_BG, 0x839affe0); + GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + GuiSetStyle(SPINNER, SELECT_BUTTON_WIDTH, 20); + GuiSetStyle(SPINNER, SELECT_BUTTON_PADDING, 2); + GuiSetStyle(SPINNER, SELECT_BUTTON_BORDER_WIDTH, 1); GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); - GuiSetStyle(SCROLLBAR, SHOW_SPINNER_BUTTONS, 0); - GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); + GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0); GuiSetStyle(SCROLLBAR, INNER_PADDING, 0); + GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); GuiSetStyle(SCROLLBAR, SLIDER_PADDING, 0); GuiSetStyle(SCROLLBAR, SLIDER_SIZE, 16); GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 10); + GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, 0x1e); + GuiSetStyle(LISTVIEW, ELEMENTS_PADDING, 2); + GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 10); + GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); + GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 6); + GuiSetStyle(COLORPICKER, BAR_WIDTH, 0x14); + GuiSetStyle(COLORPICKER, BAR_PADDING, 0xa); + GuiSetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT, 6); + GuiSetStyle(COLORPICKER, BAR_SELECTOR_PADDING, 2); } // Updates controls style with default values @@ -3049,15 +4260,15 @@ RAYGUIDEF void GuiUpdateStyleComplete(void) } // Get text with icon id prepended -// NOTE: Useful to add icons by name id (enum) instead of +// NOTE: Useful to add icons by name id (enum) instead of // a number that can change between ricon versions RAYGUIDEF const char *GuiIconText(int iconId, const char *text) { static char buffer[1024] = { 0 }; memset(buffer, 0, 1024); - + sprintf(buffer, "#%03i#", iconId); - + if (text != NULL) { for (int i = 5; i < 1024; i++) @@ -3066,7 +4277,7 @@ RAYGUIDEF const char *GuiIconText(int iconId, const char *text) if (text[i - 5] == '\0') break; } } - + return buffer; } @@ -3284,7 +4495,7 @@ static Color Fade(Color color, float alpha) if (alpha < 0.0f) alpha = 0.0f; else if (alpha > 1.0f) alpha = 1.0f; - return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)}; + return RAYGUI_CLITERAL(Color){ color.r, color.g, color.b, (unsigned char)(255.0f*alpha) }; } // Formatting of text with variables to 'embed' @@ -3301,6 +4512,30 @@ static const char *TextFormat(const char *text, ...) return buffer; } + +// Draw rectangle filled with color +static void DrawRectangleRec(Rectangle rec, Color color) +{ + DrawRectangle(rec.x, rec.y, rec.width, rec.height, color); +} + +// Draw rectangle border lines with color +static void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) +{ + DrawRectangle(rec.x, rec.y, rec.width, lineThick, color); + DrawRectangle(rec.x, rec.y + lineThick, lineThick, rec.height - 2*lineThick, color); + DrawRectangle(rec.x + rec.width - lineThick, rec.y + lineThick, lineThick, rec.height - 2*lineThick, color); + DrawRectangle(rec.x, rec.y + rec.height - lineThick, rec.width, lineThick, color); +} + +// Draw rectangle with vertical gradient fill color +// NOTE: This function is only used by GuiColorPicker() +static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2) +{ + Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height }; + DrawRectangleGradientEx(bounds, color1, color2, color2, color1); +} + #endif // RAYGUI_STANDALONE -#endif // RAYGUI_IMPLEMENTATION +#endif // RAYGUI_IMPLEMENTATION \ No newline at end of file