diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 02d852372..903ad9d69 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: # soon +github: raysan5 patreon: raylib open_collective: # Replace with a single Open Collective username ko_fi: raysan diff --git a/examples/Makefile b/examples/Makefile index 2731ee503..f57888ace 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -243,7 +243,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # logic to a self contained function: UpdateDrawFrame(), check core_basic_window_web.c for reference. # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif @@ -365,6 +365,7 @@ EXAMPLES = \ core/core_window_letterbox \ core/core_drop_files \ core/core_random_values \ + core/core_scissor_test \ core/core_storage_values \ core/core_vr_simulator \ core/core_loading_thread \ @@ -394,6 +395,7 @@ EXAMPLES = \ text/text_rectangle_bounds \ text/text_unicode \ textures/textures_logo_raylib \ + textures/textures_mouse_painting \ textures/textures_rectangle \ textures/textures_srcrec_dstrec \ textures/textures_image_drawing \ diff --git a/examples/core/core_scissor_test.c b/examples/core/core_scissor_test.c new file mode 100644 index 000000000..55221330e --- /dev/null +++ b/examples/core/core_scissor_test.c @@ -0,0 +1,71 @@ +/******************************************************************************************* +* +* raylib [core] example - Scissor test +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2019 Chris Dill (@MysteriousSpace) +* +********************************************************************************************/ + +#include "raylib.h" + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - scissor test"); + + Rectangle scissorArea = { 0, 0, 300, 300 }; + bool scissorMode = true; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_S)) scissorMode = !scissorMode; + + // Centre the scissor area around the mouse position + scissorArea.x = GetMouseX() - scissorArea.width/2; + scissorArea.y = GetMouseY() - scissorArea.height/2; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (scissorMode) BeginScissorMode(scissorArea.x, scissorArea.y, scissorArea.width, scissorArea.height); + + // Draw full screen rectangle and some text + // NOTE: Only part defined by scissor area will be rendered + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), RED); + DrawText("Move the mouse around to reveal this text!", 190, 200, 20, LIGHTGRAY); + + if (scissorMode) EndScissorMode(); + + DrawRectangleLinesEx(scissorArea, 1, BLACK); + DrawText("Press S to toggle scissor test", 10, 10, 20, BLACK); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_scissor_test.png b/examples/core/core_scissor_test.png new file mode 100644 index 000000000..194872bb7 Binary files /dev/null and b/examples/core/core_scissor_test.png differ diff --git a/examples/core/core_window_letterbox.c b/examples/core/core_window_letterbox.c index 7ee1a8328..fe67fe0a0 100644 --- a/examples/core/core_window_letterbox.c +++ b/examples/core/core_window_letterbox.c @@ -29,7 +29,7 @@ int main(void) int gameScreenWidth = 640; int gameScreenHeight = 480; - // Render texture initialization + // Render texture initialization, used to hold the rendering result so we can easily resize it RenderTexture2D target = LoadRenderTexture(gameScreenWidth, gameScreenHeight); SetTextureFilter(target.texture, FILTER_BILINEAR); // Texture scale filter to use @@ -59,7 +59,7 @@ int main(void) BeginDrawing(); ClearBackground(BLACK); - // Draw everything in the render texture + // Draw everything in the render texture, note this will not be rendered on screen, yet BeginTextureMode(target); ClearBackground(RAYWHITE); // Clear render texture background color diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 3277d7eaa..226ce8f2c 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,13 +34,16 @@ * - SliderBar --> Slider * - ProgressBar * - StatusBar +* - ScrollBar * - ScrollPanel +* - DummyRec +* - Grid +* +* # Advance Controls * - ListView --> ListElement * - ColorPicker --> ColorPanel, ColorBarHue -* - MessageBox -* - DummyRec -* - ScrollBar -* - Grid +* - MessageBox --> Label, Button +* - TextInputBox --> Label, TextBox, Button * * It also provides a set of functions for styling the controls based on its properties (size, color). * @@ -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,7 @@ #ifndef RAYGUI_H #define RAYGUI_H -#define RAYGUI_VERSION "2.0-dev" +#define RAYGUI_VERSION "2.5-dev" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -134,11 +152,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 +198,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 -// Gui global state enum +#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 control state typedef enum { GUI_STATE_NORMAL = 0, GUI_STATE_FOCUSED, @@ -198,14 +226,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 +244,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 +273,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 +304,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,14 +320,33 @@ 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; + +typedef enum { + SELECT_BUTTON_WIDTH = 16, + SELECT_BUTTON_PADDING, + SELECT_BUTTON_BORDER_WIDTH +} GuiSpinnerProperty; + +// ScrollBar +typedef enum { + ARROWS_SIZE = 16, + SLIDER_PADDING, + SLIDER_SIZE, + SCROLL_SPEED, + ARROWS_VISIBLE +} GuiScrollBarProperty; + +// ScrollBar side +typedef enum { + SCROLLBAR_LEFT_SIDE = 0, + SCROLLBAR_RIGHT_SIDE +} GuiScrollBarSide; // ListView typedef enum { @@ -315,20 +356,14 @@ typedef enum { SCROLLBAR_SIDE, // This property defines vertical scrollbar side (SCROLLBAR_LEFT_SIDE or SCROLLBAR_RIGHT_SIDE) } GuiListViewProperty; -// ScrollBar +// ColorPicker typedef enum { - ARROWS_SIZE = 16, - SLIDER_PADDING, - SLIDER_SIZE, - SCROLL_SPEED, - SHOW_SPINNER_BUTTONS -} GuiScrollBarProperty; - -// ScrollBar side -typedef enum { - SCROLLBAR_LEFT_SIDE = 0, - SCROLLBAR_RIGHT_SIDE -} GuiScrollBarSide; + 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 +387,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 +434,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 +472,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 +484,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 +517,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 +538,47 @@ static bool guiStyleLoaded = false; #define KEY_ENTER 257 #define MOUSE_LEFT_BUTTON 0 -#ifdef __cplusplus - #define CLITERAL -#else - #define CLITERAL (Color) -#endif +// Input required functions +//------------------------------------------------------------------------------- +static Vector2 GetMousePosition(void); +static int GetMouseWheelMove(void); +static bool IsMouseButtonDown(int button); +static bool IsMouseButtonPressed(int button); +static bool IsMouseButtonReleased(int button); -#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() +static bool IsKeyDown(int key); +static bool IsKeyPressed(int key); +static int GetKeyPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox() +//------------------------------------------------------------------------------- -// raylib functions are already implemented in raygui +// Drawing required functions +//------------------------------------------------------------------------------- +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() +//------------------------------------------------------------------------------- + +// 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 Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount); // -- GuiLoadStyle() +//------------------------------------------------------------------------------- + +// raylib functions already implemented in raygui //------------------------------------------------------------------------------- static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value static int ColorToInt(Color color); // Returns hexadecimal value for a Color static Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' -//------------------------------------------------------------------------------- -// 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() -//------------------------------------------------------------------------------- - -// 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 DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { /* TODO */ } // -- GuiDropdownBox() -static void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) { /* TODO */ } // -- GuiScrollBar() - -static void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint) { } // -- GuiImageButtonEx() -//------------------------------------------------------------------------------- - -// Text required functions -//------------------------------------------------------------------------------- -static Font GetFontDefault(void); // -- GetTextWidth() - -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 +598,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 +621,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 +658,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 +684,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 +714,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 +753,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 +779,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 +858,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 +872,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 +881,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 +903,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 +968,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 +1009,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 +1043,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 +1150,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 +1181,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 +1281,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 +1314,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 +1355,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; @@ -1325,7 +1423,7 @@ RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, b // Draw control //-------------------------------------------------------------------- - // TODO: Review this ugly hack... DROPDOWNBOX depends on GiListElement() that uses DEFAULT_TEXT_ALIGNMENT + // 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)); @@ -1336,33 +1434,33 @@ RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, b 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); + GuiListElement(RAYGUI_CLITERAL(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); + GuiListElement(RAYGUI_CLITERAL(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) GuiListElement(RAYGUI_CLITERAL(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); + 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((Rectangle){ bounds.x, bounds.y + bounds.height*(i + 1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), - bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, + 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((Rectangle){ bounds.x, bounds.y + bounds.height*(i+1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), - bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, + 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; @@ -1377,23 +1475,1035 @@ RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, b 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); + GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); } break; default: break; } 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 }, + // 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)) + { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(0)) pressed = true; + } + + if (active && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) + { + // If active copy all text to clipboard even when disabled + + // Backup textbox state + int select = guiTextBoxState.select; + int cursor = guiTextBoxState.cursor; + int start = guiTextBoxState.start; + if (guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor) + { + // If no selection then mark all text to be copied to clipboard + GuiTextBoxSelectAll(text); + } + + GuiTextBoxCopy(text); + + // Restore textbox state + guiTextBoxState.select = select; + guiTextBoxState.cursor = cursor; + guiTextBoxState.start = start; + } + } + } + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); + + if (state == GUI_STATE_PRESSED) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); + if (editMode && active && ((framesCounter/TEXTEDIT_CURSOR_BLINK_FRAMES)%2 == 0) && selLength == 0) + { + // Draw the blinking cursor + DrawRectangle(cursorPos.x, cursorPos.y, 1, GuiGetStyle(DEFAULT, TEXT_SIZE), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } + } + 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! RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) @@ -1401,10 +2511,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 +2527,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 +2587,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 +2647,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 +2702,7 @@ RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editM { text[keyCount] = (char)key; keyCount++; + text[keyCount] = '\0'; } } } @@ -1636,11 +2749,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 +2764,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 +2778,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 +2935,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 +2952,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 +3018,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 +3035,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 +3080,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 +3100,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 +3143,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 +3161,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 +3196,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 +3205,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 +3222,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 +3231,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 < 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 - {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, - {spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + spinnerSize/2 }, - {spinnerDownRight.x + padding, spinnerDownRight.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 - {spinnerUpLeft.x + spinnerSize/2, spinnerUpLeft.y + padding}, - {spinnerUpLeft.x + padding, spinnerUpLeft.y + spinnerSize - padding}, - {spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.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 - {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, - {spinnerDownRight.x + spinnerSize/2, spinnerDownRight.y + spinnerSize - padding }, - {spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + 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 +3401,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 +3496,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 +3504,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 +3562,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 +3581,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 +3595,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 +3612,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 +3628,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 +3639,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 +3673,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 +3688,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 +3717,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 +3745,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)); + 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)); + } + } - DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), 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 +3782,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 +3824,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 +3856,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 +3875,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 +3896,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 +3904,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 +3963,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 +3986,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 +4122,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); } @@ -2929,7 +4135,7 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) font.chars = (CharInfo *)calloc(font.charsCount, sizeof(CharInfo)); for (int i = 0; i < font.charsCount; i++) { - fread(&font.chars[i].rec, 1, sizeof(Rectangle), rgsFile); + fread(&font.recs[i], 1, sizeof(Rectangle), rgsFile); fread(&font.chars[i].value, 1, sizeof(int), rgsFile); fread(&font.chars[i].offsetX, 1, sizeof(int), rgsFile); fread(&font.chars[i].offsetY, 1, sizeof(int), rgsFile); @@ -2944,7 +4150,7 @@ RAYGUIDEF void GuiLoadStyle(const char *fileName) } #endif } - + fclose(rgsFile); } } @@ -2958,7 +4164,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 +4200,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 +4223,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(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, 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); - GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, 0x1e); - GuiSetStyle(LISTVIEW, ELEMENTS_PADDING, 2); - GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 10); - GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); - GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); - GuiSetStyle(SCROLLBAR, SHOW_SPINNER_BUTTONS, 0); - GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6); - GuiSetStyle(SCROLLBAR, INNER_PADDING, 0); - GuiSetStyle(SCROLLBAR, SLIDER_PADDING, 0); - GuiSetStyle(SCROLLBAR, SLIDER_SIZE, 16); - GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 10); } // Updates controls style with default values @@ -3049,15 +4259,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 +4276,7 @@ RAYGUIDEF const char *GuiIconText(int iconId, const char *text) if (text[i - 5] == '\0') break; } } - + return buffer; } @@ -3284,7 +4494,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 +4511,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 diff --git a/examples/textures/textures_mouse_painting.c b/examples/textures/textures_mouse_painting.c new file mode 100644 index 000000000..0149176c5 --- /dev/null +++ b/examples/textures/textures_mouse_painting.c @@ -0,0 +1,210 @@ +/******************************************************************************************* +* +* raylib [textures] example - Mouse painting +* +* This example has been created using raylib 2.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2019 Chris Dill (@MysteriousSpace) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MAX_COLORS_COUNT 23 // Number of colors available + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - mouse painting"); + + // Colours to choose from + Color colors[MAX_COLORS_COUNT] = { + RAYWHITE, YELLOW, GOLD, ORANGE, PINK, RED, MAROON, GREEN, LIME, DARKGREEN, + SKYBLUE, BLUE, DARKBLUE, PURPLE, VIOLET, DARKPURPLE, BEIGE, BROWN, DARKBROWN, + LIGHTGRAY, GRAY, DARKGRAY, BLACK }; + + // Define colorsRecs data (for every rectangle) + Rectangle colorsRecs[MAX_COLORS_COUNT] = { 0 }; + + for (int i = 0; i < MAX_COLORS_COUNT; i++) + { + colorsRecs[i].x = 10 + 30*i + 2*i; + colorsRecs[i].y = 10; + colorsRecs[i].width = 30; + colorsRecs[i].height = 30; + } + + int colorSelected = 0; + int colorSelectedPrev = colorSelected; + int colorMouseHover = 0; + int brushSize = 20; + + Rectangle btnSaveRec = { 750, 10, 40, 30 }; + bool btnSaveMouseHover = false; + bool showSaveMessage = false; + int saveMessageCounter = 0; + + // Create a RenderTexture2D to use as a canvas + RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); + + // Clear render texture before entering the game loop + BeginTextureMode(target); + ClearBackground(colors[0]); + EndTextureMode(); + + SetTargetFPS(120); // Set our game to run at 120 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + Vector2 mousePos = GetMousePosition(); + + // Move between colors with keys + if (IsKeyPressed(KEY_RIGHT)) colorSelected++; + else if (IsKeyPressed(KEY_LEFT)) colorSelected--; + + if (colorSelected >= MAX_COLORS_COUNT) colorSelected = MAX_COLORS_COUNT - 1; + else if (colorSelected < 0) colorSelected = 0; + + // Choose color with mouse + for (int i = 0; i < MAX_COLORS_COUNT; i++) + { + if (CheckCollisionPointRec(mousePos, colorsRecs[i])) + { + colorMouseHover = i; + break; + } + else colorMouseHover = -1; + } + + if ((colorMouseHover >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + colorSelected = colorMouseHover; + colorSelectedPrev = colorSelected; + } + + // Change brush size + brushSize += GetMouseWheelMove()*5; + if (brushSize < 2) brushSize = 2; + if (brushSize > 50) brushSize = 50; + + if (IsKeyPressed(KEY_C)) + { + // Clear render texture to clear color + BeginTextureMode(target); + ClearBackground(colors[0]); + EndTextureMode(); + } + + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + // Paint circle into render texture + // NOTE: To avoid discontinuous circles, we could store + // previous-next mouse points and just draw a line using brush size + BeginTextureMode(target); + if (mousePos.y > 50) DrawCircle(mousePos.x, mousePos.y, brushSize, colors[colorSelected]); + EndTextureMode(); + } + + if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) + { + colorSelected = 0; + + // Erase circle from render texture + BeginTextureMode(target); + if (mousePos.y > 50) DrawCircle(mousePos.x, mousePos.y, brushSize, colors[0]); + EndTextureMode(); + } + else colorSelected = colorSelectedPrev; + + // Check mouse hover save button + if (CheckCollisionPointRec(mousePos, btnSaveRec)) btnSaveMouseHover = true; + else btnSaveMouseHover = false; + + // Image saving logic + // NOTE: Saving painted texture to a default named image + if ((btnSaveMouseHover && IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) || IsKeyPressed(KEY_S)) + { + Image image = GetTextureData(target.texture); + ImageFlipVertical(&image); + ExportImage(image, "my_amazing_texture_painting.png"); + UnloadImage(image); + showSaveMessage = true; + } + + if (showSaveMessage) + { + // On saving, show a full screen message for 2 seconds + saveMessageCounter++; + if (saveMessageCounter > 240) + { + showSaveMessage = false; + saveMessageCounter = 0; + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom) + DrawTextureRec(target.texture, (Rectangle){ 0, 0, target.texture.width, -target.texture.height }, (Vector2){ 0, 0 }, WHITE); + + // Draw drawing circle for reference + if (mousePos.y > 50) + { + if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) DrawCircleLines(mousePos.x, mousePos.y, brushSize, colors[colorSelected]); + else DrawCircle(GetMouseX(), GetMouseY(), brushSize, colors[colorSelected]); + } + + // Draw top panel + DrawRectangle(0, 0, GetScreenWidth(), 50, RAYWHITE); + DrawLine(0, 50, GetScreenWidth(), 50, LIGHTGRAY); + + // Draw color selection rectangles + for (int i = 0; i < MAX_COLORS_COUNT; i++) DrawRectangleRec(colorsRecs[i], colors[i]); + DrawRectangleLines(10, 10, 30, 30, LIGHTGRAY); + + if (colorMouseHover >= 0) DrawRectangleRec(colorsRecs[colorMouseHover], Fade(WHITE, 0.6f)); + + DrawRectangleLinesEx((Rectangle){ colorsRecs[colorSelected].x - 2, colorsRecs[colorSelected].y - 2, + colorsRecs[colorSelected].width + 4, colorsRecs[colorSelected].height + 4 }, 2, BLACK); + + // Draw save image button + DrawRectangleLinesEx(btnSaveRec, 2, btnSaveMouseHover? RED : BLACK); + DrawText("SAVE!", 755, 20, 10, btnSaveMouseHover? RED : BLACK); + + // Draw save image message + if (showSaveMessage) + { + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, 0.8f)); + DrawRectangle(0, 150, GetScreenWidth(), 80, BLACK); + DrawText("IMAGE SAVED: my_amazing_texture_painting.png", 150, 180, 20, RAYWHITE); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(target); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/textures/textures_mouse_painting.png b/examples/textures/textures_mouse_painting.png new file mode 100644 index 000000000..a3dec5dad Binary files /dev/null and b/examples/textures/textures_mouse_painting.png differ diff --git a/games/Makefile b/games/Makefile index 0aecafb27..9b06cc0a4 100644 --- a/games/Makefile +++ b/games/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/cat_vs_roomba/Makefile b/games/cat_vs_roomba/Makefile index 87b052d26..00041d1d8 100644 --- a/games/cat_vs_roomba/Makefile +++ b/games/cat_vs_roomba/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/drturtle/Makefile b/games/drturtle/Makefile index 42658af07..5368393f9 100644 --- a/games/drturtle/Makefile +++ b/games/drturtle/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/just_do/Makefile b/games/just_do/Makefile index 7b2d4d5fa..ee4de401f 100644 --- a/games/just_do/Makefile +++ b/games/just_do/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/koala_seasons/Makefile b/games/koala_seasons/Makefile index 4bc40ca9c..fea4437d8 100644 --- a/games/koala_seasons/Makefile +++ b/games/koala_seasons/Makefile @@ -200,7 +200,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) # resource file contains windows executable icon and properties # -Wl,--subsystem,windows hides the console window - CFLAGS += $(RAYLIB_PATH)/raylib.rc.data -Wl,--subsystem,windows + CFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data -Wl,--subsystem,windows endif ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),STATIC) @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/koala_seasons/koala_seasons.c b/games/koala_seasons/koala_seasons.c index ebd552ec1..ad7892b88 100644 --- a/games/koala_seasons/koala_seasons.c +++ b/games/koala_seasons/koala_seasons.c @@ -59,9 +59,9 @@ int main(void) atlas02 = LoadTexture("resources/graphics/atlas02.png"); #if defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_ANDROID) - colorBlend = LoadShader("resources/shaders/glsl100/base.vs", "resources/shaders/glsl100/blend_color.fs"); + colorBlend = LoadShader(0, "resources/shaders/glsl100/blend_color.fs"); #else - colorBlend = LoadShader("resources/shaders/glsl330/base.vs", "resources/shaders/glsl330/blend_color.fs"); + colorBlend = LoadShader(0, "resources/shaders/glsl330/blend_color.fs"); #endif InitAudioDevice(); @@ -76,20 +76,14 @@ int main(void) fxDieDingo = LoadSound("resources/audio/dingo_die.ogg"); fxDieOwl = LoadSound("resources/audio/owl_die.ogg"); - music = LoadMusicStream("resources/audio/jngl.xm"); PlayMusicStream(music); - SetMusicVolume(music, 1.0f); + SetMusicVolume(music, 2.0f); // Define and init first screen // NOTE: currentScreen is defined in screens.h as a global variable currentScreen = TITLE; - - InitLogoScreen(); - //InitOptionsScreen(); InitTitleScreen(); - InitGameplayScreen(); - InitEndingScreen(); #if defined(PLATFORM_WEB) emscripten_set_main_loop(UpdateDrawFrame, 0, 1); @@ -257,9 +251,7 @@ void UpdateDrawFrame(void) } if (onTransition) DrawTransition(); - - DrawFPS(20, GetScreenHeight() - 30); - + DrawRectangle(GetScreenWidth() - 200, GetScreenHeight() - 50, 200, 40, Fade(WHITE, 0.6f)); DrawText("ALPHA VERSION", GetScreenWidth() - 180, GetScreenHeight() - 40, 20, DARKGRAY); diff --git a/games/koala_seasons/screens/screen_gameplay.c b/games/koala_seasons/screens/screen_gameplay.c index 4d8ff04d6..0bbad362d 100644 --- a/games/koala_seasons/screens/screen_gameplay.c +++ b/games/koala_seasons/screens/screen_gameplay.c @@ -38,7 +38,6 @@ //#define DEBUG -// DONE: Review MAX_* limits, don't waste memory!!! #define MAX_ENEMIES 16 #define MAX_BAMBOO 16 #define MAX_LEAVES 14 @@ -96,7 +95,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef enum { WINTER, SPRING, SUMMER, FALL, TRANSITION} SeasonState; +typedef enum { WINTER, SPRING, SUMMER, FALL, TRANSITION } SeasonState; typedef enum { JUMPING, KICK, FINALFORM, GRABED, ONWIND } KoalaState; typedef struct { @@ -343,7 +342,6 @@ static Rectangle leftButton = {0, 0, 0, 0}; static Rectangle rightButton = {0, 0, 0, 0}; static Rectangle powerButton = {0, 0, 0, 0}; static Rectangle fire[MAX_FIRE]; -//static Rectangle flames[MAX_FLAMES]; static Rectangle ice[MAX_ICE]; static Rectangle resin[MAX_RESIN]; static Rectangle wind[MAX_WIND]; @@ -351,7 +349,7 @@ static Rectangle bamboo[MAX_BAMBOO]; static Rectangle snake[MAX_ENEMIES]; static Rectangle dingo[MAX_ENEMIES]; static Rectangle owl[MAX_ENEMIES]; -static Rectangle leaf[MAX_LEAVES]; // DONE: Review name! +static Rectangle leaf[MAX_LEAVES]; static Rectangle powerBar; static Rectangle backBar; static Rectangle fireAnimation; @@ -387,7 +385,7 @@ static Vector2 textSize; static Vector2 clockPosition; static Particle enemyHit[MAX_ENEMIES]; -static ParticleSystem leafParticles[MAX_LEAVES]; // DONE: Review!!! Creating 40 ParticleSystem!!! -> 40*128 = 5120 Particles! Maybe better create a struct Leaf? +static ParticleSystem leafParticles[MAX_LEAVES]; static ParticleSystem snowParticle; static ParticleSystem backSnowParticle; static ParticleSystem dandelionParticle; @@ -670,7 +668,7 @@ void UpdateGameplayScreen(void) transitionFramesCounter += speedMod*TIME_FACTOR; - if(transitionFramesCounter <= SEASONTRANSITION) + if (transitionFramesCounter <= SEASONTRANSITION) { color00 = ColorTransition(initcolor00, finalcolor00, transitionFramesCounter); color01 = ColorTransition(initcolor01, finalcolor01, transitionFramesCounter); @@ -1043,12 +1041,8 @@ void UpdateGameplayScreen(void) #endif } #if defined(DEBUG) - if (currentLeaves < LEAVESTOTRANSFORM && (IsKeyPressed(KEY_ENTER))) - { - currentLeaves += LEAVESTOTRANSFORM; - } + if ((currentLeaves < LEAVESTOTRANSFORM) && (IsKeyPressed(KEY_ENTER))) currentLeaves += LEAVESTOTRANSFORM; #endif - if (coolDown) { power += 20; @@ -1427,10 +1421,6 @@ void UpdateGameplayScreen(void) if (CheckCollisionRecs(player, leaf[j]) && leafActive[j]) { - //power += 20; - //printf("coin %c", coinType[j]); - - // DONE: Review popupLeaves[j].position = (Vector2){ leaf[j].x, leaf[j].y }; popupLeaves[j].scale = 1.0f; popupLeaves[j].alpha = 1.0f; @@ -1438,25 +1428,25 @@ void UpdateGameplayScreen(void) PlaySound(fxEatLeaves); - if(leafType[j] == 0) + if (leafType[j] == 0) { currentLeaves++; popupLeaves[j].score = 1; } - else if(leafType[j] == 1) + else if (leafType[j] == 1) { currentLeaves += 2; popupLeaves[j].score = 2; } - else if(leafType[j] == 2) + else if (leafType[j] == 2) { currentLeaves += 3; popupLeaves[j].score = 3; } - else if(leafType[j] == 3) + else if (leafType[j] == 3) { currentLeaves += 4; popupLeaves[j].score = 4; @@ -2232,7 +2222,6 @@ void UpdateGameplayScreen(void) player.x -= speed; grabCounter += 1*TIME_FACTOR; - // DONE: Review, before checking collision with ALL enemies, check if they are active! for (int i = 0; i < MAX_ENEMIES; i++) { if (CheckCollisionRecs(player, snake[i]) && !isHitSnake[i] && snakeActive[i]) @@ -2286,7 +2275,6 @@ void UpdateGameplayScreen(void) enemyHit[i].speed = (Vector2){ dingo[i].x, dingo[i].y }; enemyHit[i].size = (float)GetRandomValue(5, 10)/30; enemyHit[i].rotation = 0.0f; - //enemyHit[i].color = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }; enemyHit[i].alpha = 1.0f; enemyHit[i].active = true; @@ -2317,7 +2305,6 @@ void UpdateGameplayScreen(void) enemyHit[i].speed = (Vector2){ owl[i].x, owl[i].y }; enemyHit[i].size = (float)GetRandomValue(5, 10)/30; enemyHit[i].rotation = 0.0f; - //enemyHit[i].color = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }; enemyHit[i].alpha = 1.0f; enemyHit[i].active = true; @@ -2392,9 +2379,7 @@ void UpdateGameplayScreen(void) thisFrameKoala = 0; } - //if (curFrameKoala > 1) curFrameKoala = ; - - if(curFrameKoala <= 1 )koalaAnimationTransform.x = gameplay_koala_transform.x + koalaAnimationTransform.width*curFrameKoala; + if (curFrameKoala <= 1) koalaAnimationTransform.x = gameplay_koala_transform.x + koalaAnimationTransform.width*curFrameKoala; if (transAniCounter >= 5) { @@ -2407,8 +2392,7 @@ void UpdateGameplayScreen(void) finalColor = RED; finalColor2 = WHITE; } - - if (!transBackAnim) + else { finalColor = WHITE; finalColor2 = RED; @@ -2420,9 +2404,7 @@ void UpdateGameplayScreen(void) thisFrameKoala = 0; curFrameKoala = 0; speedFX.active = true; - //speedMod = 2; transCount = 0; - //printf ("THIS ISN'T EVEN MY FINAL FORM"); bambooTimer += 15*TIME_FACTOR; } } @@ -2558,7 +2540,7 @@ void UpdateGameplayScreen(void) velocity -= 1*TIME_FACTOR; player.y -= velocity; - if(player.y >= GetScreenHeight()) + if (player.y >= GetScreenHeight()) { deathsCounter++; finishScreen = 1; @@ -2781,7 +2763,7 @@ void DrawGameplayScreen(void) case KICK:DrawTexturePro(atlas01, gameplay_koala_dash, (Rectangle){player.x - player.width, player.y - gameplay_koala_jump.height/4, gameplay_koala_dash.width, gameplay_koala_dash.height}, (Vector2){0, 0}, 0, WHITE); break; case FINALFORM: { - if(transforming)DrawTexturePro(atlas01, koalaAnimationTransform, (Rectangle){player.x - player.width, player.y - gameplay_koala_transform.height/4, gameplay_koala_transform.width/2, gameplay_koala_transform.height}, (Vector2){0, 0}, 0, finalColor); + if (transforming)DrawTexturePro(atlas01, koalaAnimationTransform, (Rectangle){player.x - player.width, player.y - gameplay_koala_transform.height/4, gameplay_koala_transform.width/2, gameplay_koala_transform.height}, (Vector2){0, 0}, 0, finalColor); else DrawTexturePro(atlas01, koalaAnimationFly, (Rectangle){player.x - gameplay_koala_fly.width/3, player.y - gameplay_koala_fly.height/4, gameplay_koala_fly.width/2, gameplay_koala_fly.height}, (Vector2){0, 0}, 0, finalColor);//DrawTextureRec((koalaFly), (Rectangle){0, 0, 128, 128}, (Vector2){player.x - 50, player.y - 40}, WHITE); } break; @@ -3781,7 +3763,7 @@ static void Reset(void) bamboo[i].y = 0; bamboo[i].width = 50; bamboo[i].height = GetScreenHeight(); - if(i > 5) bambooActive[i] = false; + if (i > 5) bambooActive[i] = false; else bambooActive[i] = true; } diff --git a/games/light_my_ritual/Makefile b/games/light_my_ritual/Makefile index aed79bb46..7c6a14387 100644 --- a/games/light_my_ritual/Makefile +++ b/games/light_my_ritual/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/skully_escape/Makefile b/games/skully_escape/Makefile index 44dcd3919..c785eb280 100644 --- a/games/skully_escape/Makefile +++ b/games/skully_escape/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/transmission/Makefile b/games/transmission/Makefile index 5b6922f0c..6df64277b 100644 --- a/games/transmission/Makefile +++ b/games/transmission/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/games/wave_collector/Makefile b/games/wave_collector/Makefile index d1c6c0960..894cc7e7d 100644 --- a/games/wave_collector/Makefile +++ b/games/wave_collector/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/projects/4coder/Makefile b/projects/4coder/Makefile index a00f4fab0..08d4da59a 100644 --- a/projects/4coder/Makefile +++ b/projects/4coder/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/projects/VSCode/.vscode/c_cpp_properties.json b/projects/VSCode/.vscode/c_cpp_properties.json index 496a9b2df..10881ee8a 100644 --- a/projects/VSCode/.vscode/c_cpp_properties.json +++ b/projects/VSCode/.vscode/c_cpp_properties.json @@ -14,9 +14,9 @@ "PLATFORM_DESKTOP" ], "compilerPath": "C:/raylib/mingw/bin/gcc.exe", - "cStandard": "c11", + "cStandard": "c99", "cppStandard": "c++14", - "intelliSenseMode": "clang-x64" + "intelliSenseMode": "gcc-x64" }, { "name": "Mac", diff --git a/projects/VSCode/.vscode/tasks.json b/projects/VSCode/.vscode/tasks.json index 70337b6f7..e701baad0 100644 --- a/projects/VSCode/.vscode/tasks.json +++ b/projects/VSCode/.vscode/tasks.json @@ -26,7 +26,10 @@ "group": { "kind": "build", "isDefault": true - } + }, + "problemMatcher": [ + "$gcc" + ] }, { "label": "build release", @@ -47,7 +50,10 @@ "RAYLIB_PATH=/raylib", ], }, - "group": "build" + "group": "build", + "problemMatcher": [ + "$gcc" + ] } ] } diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index f29b22fb8..13a2853ba 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -29,8 +29,10 @@ RAYLIB_VERSION ?= 2.5.0 RAYLIB_API_VERSION ?= 251 RAYLIB_PATH ?= ..\.. -# Define default options +# Define compiler path on Windows +COMPILER_PATH ?= C:/raylib/mingw/bin +# Define default options # One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP @@ -68,6 +70,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) PLATFORM_OS=WINDOWS + export PATH := $(COMPILER_PATH):$(PATH) else UNAMEOS=$(shell uname) ifeq ($(UNAMEOS),Linux) @@ -236,7 +239,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/src/camera.h b/src/camera.h index a933447d5..bc813b53b 100644 --- a/src/camera.h +++ b/src/camera.h @@ -251,10 +251,6 @@ void SetCameraMode(Camera camera, int mode) cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW) - // NOTE: Just testing what cameraAngle means - //cameraAngle.x = 0.0f*DEG2RAD; // Camera angle in plane XZ (0 aligned with Z, move positive CCW) - //cameraAngle.y = -60.0f*DEG2RAD; // Camera angle in plane XY (0 aligned with X, move positive CW) - playerEyesPosition = camera.position.y; // Lock cursor for first person and third person cameras diff --git a/src/core.c b/src/core.c index 416b68305..6bb12619a 100644 --- a/src/core.c +++ b/src/core.c @@ -126,7 +126,7 @@ #include "gestures.h" // Gestures detection functionality #endif -#if defined(SUPPORT_CAMERA_SYSTEM) && !defined(PLATFORM_ANDROID) +#if defined(SUPPORT_CAMERA_SYSTEM) #define CAMERA_IMPLEMENTATION #include "camera.h" // Camera system functionality #endif diff --git a/src/raudio.c b/src/raudio.c index c63c5e9d4..bfd7ef220 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -236,16 +236,16 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr // AudioBuffer management functions declaration // NOTE: Those functions are not exposed by raylib... for the moment AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 bufferSizeInFrames, int usage); -void CloseAudioBuffer(AudioBuffer *audioBuffer); -bool IsAudioBufferPlaying(AudioBuffer *audioBuffer); -void PlayAudioBuffer(AudioBuffer *audioBuffer); -void StopAudioBuffer(AudioBuffer *audioBuffer); -void PauseAudioBuffer(AudioBuffer *audioBuffer); -void ResumeAudioBuffer(AudioBuffer *audioBuffer); -void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume); -void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch); -void TrackAudioBuffer(AudioBuffer *audioBuffer); -void UntrackAudioBuffer(AudioBuffer *audioBuffer); +void CloseAudioBuffer(AudioBuffer *buffer); +bool IsAudioBufferPlaying(AudioBuffer *buffer); +void PlayAudioBuffer(AudioBuffer *buffer); +void StopAudioBuffer(AudioBuffer *buffer); +void PauseAudioBuffer(AudioBuffer *buffer); +void ResumeAudioBuffer(AudioBuffer *buffer); +void SetAudioBufferVolume(AudioBuffer *buffer, float volume); +void SetAudioBufferPitch(AudioBuffer *buffer, float pitch); +void TrackAudioBuffer(AudioBuffer *buffer); +void UntrackAudioBuffer(AudioBuffer *buffer); //---------------------------------------------------------------------------------- // Multi channel playback globals @@ -644,151 +644,127 @@ AudioBuffer *InitAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam } // Delete an audio buffer -void CloseAudioBuffer(AudioBuffer *audioBuffer) +void CloseAudioBuffer(AudioBuffer *buffer) { - if (audioBuffer == NULL) + if (buffer != NULL) { - TraceLog(LOG_ERROR, "CloseAudioBuffer() : No audio buffer"); - return; + UntrackAudioBuffer(buffer); + RL_FREE(buffer->buffer); + RL_FREE(buffer); } - - UntrackAudioBuffer(audioBuffer); - RL_FREE(audioBuffer->buffer); - RL_FREE(audioBuffer); + else TraceLog(LOG_ERROR, "CloseAudioBuffer() : No audio buffer"); } // Check if an audio buffer is playing -bool IsAudioBufferPlaying(AudioBuffer *audioBuffer) +bool IsAudioBufferPlaying(AudioBuffer *buffer) { - if (audioBuffer == NULL) - { - TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer"); - return false; - } + bool result = false; + + if (buffer != NULL) result = (buffer->playing && !buffer->paused); + else TraceLog(LOG_ERROR, "IsAudioBufferPlaying() : No audio buffer"); - return audioBuffer->playing && !audioBuffer->paused; + return result; } // Play an audio buffer // NOTE: Buffer is restarted to the start. // Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position should be maintained. -void PlayAudioBuffer(AudioBuffer *audioBuffer) +void PlayAudioBuffer(AudioBuffer *buffer) { - if (audioBuffer == NULL) + if (buffer != NULL) { - TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); - return; + buffer->playing = true; + buffer->paused = false; + buffer->frameCursorPos = 0; } - - audioBuffer->playing = true; - audioBuffer->paused = false; - audioBuffer->frameCursorPos = 0; + else TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); } // Stop an audio buffer -void StopAudioBuffer(AudioBuffer *audioBuffer) +void StopAudioBuffer(AudioBuffer *buffer) { - if (audioBuffer == NULL) + if (buffer != NULL) { - TraceLog(LOG_ERROR, "StopAudioBuffer() : No audio buffer"); - return; + if (IsAudioBufferPlaying(buffer)) + { + buffer->playing = false; + buffer->paused = false; + buffer->frameCursorPos = 0; + buffer->isSubBufferProcessed[0] = true; + buffer->isSubBufferProcessed[1] = true; + } } - - // Don't do anything if the audio buffer is already stopped. - if (!IsAudioBufferPlaying(audioBuffer)) return; - - audioBuffer->playing = false; - audioBuffer->paused = false; - audioBuffer->frameCursorPos = 0; - audioBuffer->isSubBufferProcessed[0] = true; - audioBuffer->isSubBufferProcessed[1] = true; + else TraceLog(LOG_ERROR, "StopAudioBuffer() : No audio buffer"); } // Pause an audio buffer -void PauseAudioBuffer(AudioBuffer *audioBuffer) +void PauseAudioBuffer(AudioBuffer *buffer) { - if (audioBuffer == NULL) - { - TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); - return; - } - - audioBuffer->paused = true; + if (buffer != NULL) buffer->paused = true; + else TraceLog(LOG_ERROR, "PauseAudioBuffer() : No audio buffer"); } // Resume an audio buffer -void ResumeAudioBuffer(AudioBuffer *audioBuffer) +void ResumeAudioBuffer(AudioBuffer *buffer) { - if (audioBuffer == NULL) - { - TraceLog(LOG_ERROR, "ResumeAudioBuffer() : No audio buffer"); - return; - } - - audioBuffer->paused = false; + if (buffer != NULL) buffer->paused = false; + else TraceLog(LOG_ERROR, "ResumeAudioBuffer() : No audio buffer"); } // Set volume for an audio buffer -void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume) +void SetAudioBufferVolume(AudioBuffer *buffer, float volume) { - if (audioBuffer == NULL) - { - TraceLog(LOG_WARNING, "SetAudioBufferVolume() : No audio buffer"); - return; - } - - audioBuffer->volume = volume; + if (buffer != NULL) buffer->volume = volume; + else TraceLog(LOG_WARNING, "SetAudioBufferVolume() : No audio buffer"); } // Set pitch for an audio buffer -void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch) +void SetAudioBufferPitch(AudioBuffer *buffer, float pitch) { - if (audioBuffer == NULL) + if (buffer != NULL) { - TraceLog(LOG_WARNING, "SetAudioBufferPitch() : No audio buffer"); - return; + float pitchMul = pitch/buffer->pitch; + + // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches + // will make the sound faster; lower pitches make it slower. + ma_uint32 newOutputSampleRate = (ma_uint32)((float)buffer->dsp.src.config.sampleRateOut/pitchMul); + buffer->pitch *= (float)buffer->dsp.src.config.sampleRateOut/newOutputSampleRate; + + ma_pcm_converter_set_output_sample_rate(&buffer->dsp, newOutputSampleRate); } - - float pitchMul = pitch/audioBuffer->pitch; - - // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches - // will make the sound faster; lower pitches make it slower. - ma_uint32 newOutputSampleRate = (ma_uint32)((float)audioBuffer->dsp.src.config.sampleRateOut / pitchMul); - audioBuffer->pitch *= (float)audioBuffer->dsp.src.config.sampleRateOut / newOutputSampleRate; - - ma_pcm_converter_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate); + else TraceLog(LOG_WARNING, "SetAudioBufferPitch() : No audio buffer"); } // Track audio buffer to linked list next position -void TrackAudioBuffer(AudioBuffer *audioBuffer) +void TrackAudioBuffer(AudioBuffer *buffer) { ma_mutex_lock(&audioLock); { - if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer; + if (firstAudioBuffer == NULL) firstAudioBuffer = buffer; else { - lastAudioBuffer->next = audioBuffer; - audioBuffer->prev = lastAudioBuffer; + lastAudioBuffer->next = buffer; + buffer->prev = lastAudioBuffer; } - lastAudioBuffer = audioBuffer; + lastAudioBuffer = buffer; } ma_mutex_unlock(&audioLock); } // Untrack audio buffer from linked list -void UntrackAudioBuffer(AudioBuffer *audioBuffer) +void UntrackAudioBuffer(AudioBuffer *buffer) { ma_mutex_lock(&audioLock); { - if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next; - else audioBuffer->prev->next = audioBuffer->next; + if (buffer->prev == NULL) firstAudioBuffer = buffer->next; + else buffer->prev->next = buffer->next; - if (audioBuffer->next == NULL) lastAudioBuffer = audioBuffer->prev; - else audioBuffer->next->prev = audioBuffer->prev; + if (buffer->next == NULL) lastAudioBuffer = buffer->prev; + else buffer->next->prev = buffer->prev; - audioBuffer->prev = NULL; - audioBuffer->next = NULL; + buffer->prev = NULL; + buffer->next = NULL; } ma_mutex_unlock(&audioLock); } @@ -802,10 +778,9 @@ Wave LoadWave(const char *fileName) { Wave wave = { 0 }; + if (false) { } #if defined(SUPPORT_FILEFORMAT_WAV) - if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName); -#else - if (false) {} + else if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName); #endif #if defined(SUPPORT_FILEFORMAT_OGG) else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName); @@ -821,25 +796,6 @@ Wave LoadWave(const char *fileName) return wave; } -// Load wave data from raw array data -Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels) -{ - Wave wave; - - wave.data = data; - wave.sampleCount = sampleCount; - wave.sampleRate = sampleRate; - wave.sampleSize = sampleSize; - wave.channels = channels; - - // NOTE: Copy wave data to work with, user is responsible of input data to free - Wave cwave = WaveCopy(wave); - - WaveFormat(&cwave, sampleRate, sampleSize, channels); - - return cwave; -} - // Load sound from file // NOTE: The entire file is loaded to memory to be played (no-streaming) Sound LoadSound(const char *fileName) @@ -903,7 +859,7 @@ void UnloadWave(Wave wave) // Unload sound void UnloadSound(Sound sound) { - CloseAudioBuffer((AudioBuffer *)sound.stream.buffer); + CloseAudioBuffer(sound.stream.buffer); TraceLog(LOG_INFO, "Unloaded sound data from RAM"); } @@ -911,7 +867,7 @@ void UnloadSound(Sound sound) // Update sound buffer with new data void UpdateSound(Sound sound, const void *data, int samplesCount) { - AudioBuffer *audioBuffer = (AudioBuffer *)sound.stream.buffer; + AudioBuffer *audioBuffer = sound.stream.buffer; if (audioBuffer == NULL) { @@ -930,10 +886,9 @@ void ExportWave(Wave wave, const char *fileName) { bool success = false; + if (false) { } #if defined(SUPPORT_FILEFORMAT_WAV) - if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName); -#else - if (false) {} + else if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName); #endif else if (IsFileExtension(fileName, ".raw")) { @@ -994,7 +949,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName) // Play a sound void PlaySound(Sound sound) { - PlayAudioBuffer((AudioBuffer *)sound.stream.buffer); + PlayAudioBuffer(sound.stream.buffer); } // Play a sound in the multichannel buffer pool @@ -1046,17 +1001,16 @@ void PlaySoundMulti(Sound sound) audioBufferPoolChannels[index] = audioBufferPoolCounter; audioBufferPoolCounter++; - audioBufferPool[index]->volume = ((AudioBuffer*)sound.stream.buffer)->volume; - audioBufferPool[index]->pitch = ((AudioBuffer*)sound.stream.buffer)->pitch; - audioBufferPool[index]->looping = ((AudioBuffer*)sound.stream.buffer)->looping; - audioBufferPool[index]->usage = ((AudioBuffer*)sound.stream.buffer)->usage; + audioBufferPool[index]->volume = sound.stream.buffer->volume; + audioBufferPool[index]->pitch = sound.stream.buffer->pitch; + audioBufferPool[index]->looping = sound.stream.buffer->looping; + audioBufferPool[index]->usage = sound.stream.buffer->usage; audioBufferPool[index]->isSubBufferProcessed[0] = false; audioBufferPool[index]->isSubBufferProcessed[1] = false; - audioBufferPool[index]->bufferSizeInFrames = ((AudioBuffer*)sound.stream.buffer)->bufferSizeInFrames; - audioBufferPool[index]->buffer = ((AudioBuffer*)sound.stream.buffer)->buffer; + audioBufferPool[index]->bufferSizeInFrames = sound.stream.buffer->bufferSizeInFrames; + audioBufferPool[index]->buffer = sound.stream.buffer->buffer; PlayAudioBuffer(audioBufferPool[index]); - } // Stop any sound played with PlaySoundMulti() @@ -1081,37 +1035,37 @@ int GetSoundsPlaying(void) // Pause a sound void PauseSound(Sound sound) { - PauseAudioBuffer((AudioBuffer *)sound.stream.buffer); + PauseAudioBuffer(sound.stream.buffer); } // Resume a paused sound void ResumeSound(Sound sound) { - ResumeAudioBuffer((AudioBuffer *)sound.stream.buffer); + ResumeAudioBuffer(sound.stream.buffer); } // Stop reproducing a sound void StopSound(Sound sound) { - StopAudioBuffer((AudioBuffer *)sound.stream.buffer); + StopAudioBuffer(sound.stream.buffer); } // Check if a sound is playing bool IsSoundPlaying(Sound sound) { - return IsAudioBufferPlaying((AudioBuffer *)sound.stream.buffer); + return IsAudioBufferPlaying(sound.stream.buffer); } // Set volume for a sound void SetSoundVolume(Sound sound, float volume) { - SetAudioBufferVolume((AudioBuffer *)sound.stream.buffer, volume); + SetAudioBufferVolume(sound.stream.buffer, volume); } // Set pitch for a sound void SetSoundPitch(Sound sound, float pitch) { - SetAudioBufferPitch((AudioBuffer *)sound.stream.buffer, pitch); + SetAudioBufferPitch(sound.stream.buffer, pitch); } // Convert wave data to desired format @@ -1212,53 +1166,52 @@ float *GetWaveData(Wave wave) // Load music stream from file Music LoadMusicStream(const char *fileName) { - Music music = (MusicStream *)RL_MALLOC(sizeof(MusicStream)); - bool musicLoaded = true; + Music music = { 0 }; + bool musicLoaded = false; + if (false) { } #if defined(SUPPORT_FILEFORMAT_OGG) - if (IsFileExtension(fileName, ".ogg")) + else if (IsFileExtension(fileName, ".ogg")) { // Open ogg audio stream - music->ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); + music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); - if (music->ctxData == NULL) musicLoaded = false; - else + if (music.ctxData != NULL) { - stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music->ctxData); // Get Ogg file info + music.ctxType = MUSIC_AUDIO_OGG; + stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info // OGG bit rate defaults to 16 bit, it's enough for compressed format - music->stream = InitAudioStream(info.sample_rate, 16, info.channels); - music->sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music->ctxData)*info.channels; - music->sampleLeft = music->sampleCount; - music->ctxType = MUSIC_AUDIO_OGG; - music->loopCount = 0; // Infinite loop by default + music.stream = InitAudioStream(info.sample_rate, 16, info.channels); + music.sampleCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData)*info.channels; + music.sampleLeft = music.sampleCount; + music.loopCount = 0; // Infinite loop by default + musicLoaded = true; - TraceLog(LOG_DEBUG, "[%s] OGG total samples: %i", fileName, music->sampleCount); + TraceLog(LOG_DEBUG, "[%s] OGG total samples: %i", fileName, music.sampleCount); TraceLog(LOG_DEBUG, "[%s] OGG sample rate: %i", fileName, info.sample_rate); TraceLog(LOG_DEBUG, "[%s] OGG channels: %i", fileName, info.channels); TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required); } } -#else - if (false) {} #endif #if defined(SUPPORT_FILEFORMAT_FLAC) else if (IsFileExtension(fileName, ".flac")) { - music->ctxData = drflac_open_file(fileName); + music.ctxData = drflac_open_file(fileName); - if (music->ctxData == NULL) musicLoaded = false; - else + if (music.ctxData != NULL) { - drflac *ctxFlac = (drflac *)music->ctxData; - - music->stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); - music->sampleCount = (unsigned int)ctxFlac->totalSampleCount; - music->sampleLeft = music->sampleCount; - music->ctxType = MUSIC_AUDIO_FLAC; - music->loopCount = 0; // Infinite loop by default + music.ctxType = MUSIC_AUDIO_FLAC; + drflac *ctxFlac = (drflac *)music.ctxData; - TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music->sampleCount); + music.stream = InitAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); + music.sampleCount = (unsigned int)ctxFlac->totalSampleCount; + music.sampleLeft = music.sampleCount; + music.loopCount = 0; // Infinite loop by default + musicLoaded = true; + + TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music.sampleCount); TraceLog(LOG_DEBUG, "[%s] FLAC sample rate: %i", fileName, ctxFlac->sampleRate); TraceLog(LOG_DEBUG, "[%s] FLAC bits per sample: %i", fileName, ctxFlac->bitsPerSample); TraceLog(LOG_DEBUG, "[%s] FLAC channels: %i", fileName, ctxFlac->channels); @@ -1269,24 +1222,24 @@ Music LoadMusicStream(const char *fileName) else if (IsFileExtension(fileName, ".mp3")) { drmp3 *ctxMp3 = RL_MALLOC(sizeof(drmp3)); - music->ctxData = ctxMp3; + music.ctxData = ctxMp3; int result = drmp3_init_file(ctxMp3, fileName, NULL); - if (!result) musicLoaded = false; - else + if (result > 0) { + music.ctxType = MUSIC_AUDIO_MP3; + + music.stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); + music.sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels; + music.sampleLeft = music.sampleCount; + music.loopCount = 0; // Infinite loop by default + musicLoaded = true; + TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, ctxMp3->sampleRate); TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32); TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, ctxMp3->channels); - - music->stream = InitAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); - music->sampleCount = drmp3_get_pcm_frame_count(ctxMp3)*ctxMp3->channels; - music->sampleLeft = music->sampleCount; - music->ctxType = MUSIC_AUDIO_MP3; - music->loopCount = 0; // Infinite loop by default - - TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->sampleCount); + TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music.sampleCount); } } #endif @@ -1297,73 +1250,70 @@ Music LoadMusicStream(const char *fileName) int result = jar_xm_create_context_from_file(&ctxXm, 48000, fileName); - if (!result) // XM context created successfully + if (result > 0) // XM context created successfully { - jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops + music.ctxType = MUSIC_MODULE_XM; + jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops // NOTE: Only stereo is supported for XM - music->stream = InitAudioStream(48000, 16, 2); - music->sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); - music->sampleLeft = music->sampleCount; - music->ctxType = MUSIC_MODULE_XM; - music->loopCount = 0; // Infinite loop by default - - TraceLog(LOG_INFO, "[%s] XM number of samples: %i", fileName, music->sampleCount); - TraceLog(LOG_INFO, "[%s] XM track length: %11.6f sec", fileName, (float)music->sampleCount/48000.0f); + music.stream = InitAudioStream(48000, 16, 2); + music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); + music.sampleLeft = music.sampleCount; + music.loopCount = 0; // Infinite loop by default + musicLoaded = true; - music->ctxData = ctxXm; + music.ctxData = ctxXm; + + TraceLog(LOG_INFO, "[%s] XM number of samples: %i", fileName, music.sampleCount); + TraceLog(LOG_INFO, "[%s] XM track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f); } - else musicLoaded = false; } #endif #if defined(SUPPORT_FILEFORMAT_MOD) else if (IsFileExtension(fileName, ".mod")) { jar_mod_context_t *ctxMod = RL_MALLOC(sizeof(jar_mod_context_t)); - music->ctxData = ctxMod; + music.ctxData = ctxMod; jar_mod_init(ctxMod); + int result = jar_mod_load_file(ctxMod, fileName); - if (jar_mod_load_file(ctxMod, fileName)) + if (result > 0) { - // NOTE: Only stereo is supported for MOD - music->stream = InitAudioStream(48000, 16, 2); - music->sampleCount = (unsigned int)jar_mod_max_samples(ctxMod); - music->sampleLeft = music->sampleCount; - music->ctxType = MUSIC_MODULE_MOD; - music->loopCount = 0; // Infinite loop by default + music.ctxType = MUSIC_MODULE_MOD; - TraceLog(LOG_INFO, "[%s] MOD number of samples: %i", fileName, music->sampleLeft); - TraceLog(LOG_INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music->sampleCount/48000.0f); + // NOTE: Only stereo is supported for MOD + music.stream = InitAudioStream(48000, 16, 2); + music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod); + music.sampleLeft = music.sampleCount; + music.loopCount = 0; // Infinite loop by default + musicLoaded = true; + + TraceLog(LOG_INFO, "[%s] MOD number of samples: %i", fileName, music.sampleLeft); + TraceLog(LOG_INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music.sampleCount/48000.0f); } - else musicLoaded = false; } #endif - else musicLoaded = false; if (!musicLoaded) { + if (false) { } #if defined(SUPPORT_FILEFORMAT_OGG) - if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music->ctxData); - #else - if (false) {} + else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music->ctxData); + else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_MP3) - else if (music->ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music->ctxData); RL_FREE(music->ctxData); } + else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif #if defined(SUPPORT_FILEFORMAT_XM) - else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music->ctxData); + else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_MOD) - else if (music->ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music->ctxData); RL_FREE(music->ctxData); } + else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } #endif - RL_FREE(music); - music = NULL; - TraceLog(LOG_WARNING, "[%s] Music file could not be opened", fileName); } @@ -1373,125 +1323,113 @@ Music LoadMusicStream(const char *fileName) // Unload music stream void UnloadMusicStream(Music music) { - if (music == NULL) return; - - CloseAudioStream(music->stream); + CloseAudioStream(music.stream); + if (false) { } #if defined(SUPPORT_FILEFORMAT_OGG) - if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music->ctxData); -#else - if (false) {} + else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music->ctxData); + else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_MP3) - else if (music->ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music->ctxData); RL_FREE(music->ctxData); } + else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif #if defined(SUPPORT_FILEFORMAT_XM) - else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music->ctxData); + else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_MOD) - else if (music->ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music->ctxData); RL_FREE(music->ctxData); } + else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } #endif - - RL_FREE(music); } // Start music playing (open stream) void PlayMusicStream(Music music) { - if (music != NULL) + AudioBuffer *audioBuffer = music.stream.buffer; + + if (audioBuffer == NULL) { - AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.buffer; - - if (audioBuffer == NULL) - { - TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); - return; - } - - // For music streams, we need to make sure we maintain the frame cursor position. This is hack for this section of code in UpdateMusicStream() - // // NOTE: In case window is minimized, music stream is stopped, - // // just make sure to play again on window restore - // if (IsMusicPlaying(music)) PlayMusicStream(music); - ma_uint32 frameCursorPos = audioBuffer->frameCursorPos; - - PlayAudioStream(music->stream); // <-- This resets the cursor position. - - audioBuffer->frameCursorPos = frameCursorPos; + TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); + return; } + + // For music streams, we need to make sure we maintain the frame cursor position. This is hack for this section of code in UpdateMusicStream() + // // NOTE: In case window is minimized, music stream is stopped, + // // just make sure to play again on window restore + // if (IsMusicPlaying(music)) PlayMusicStream(music); + ma_uint32 frameCursorPos = audioBuffer->frameCursorPos; + + PlayAudioStream(music.stream); // <-- This resets the cursor position. + + audioBuffer->frameCursorPos = frameCursorPos; } // Pause music playing void PauseMusicStream(Music music) { - if (music != NULL) PauseAudioStream(music->stream); + PauseAudioStream(music.stream); } // Resume music playing void ResumeMusicStream(Music music) { - if (music != NULL) ResumeAudioStream(music->stream); + ResumeAudioStream(music.stream); } // Stop music playing (close stream) void StopMusicStream(Music music) { - if (music == NULL) return; - - StopAudioStream(music->stream); + StopAudioStream(music.stream); // Restart music context - switch (music->ctxType) + switch (music.ctxType) { #if defined(SUPPORT_FILEFORMAT_OGG) - case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music->ctxData); break; + case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break; #endif #if defined(SUPPORT_FILEFORMAT_MP3) - case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music->ctxData, 0); break; + case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, 0); break; #endif #if defined(SUPPORT_FILEFORMAT_XM) - case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music->ctxData); break; + case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break; #endif #if defined(SUPPORT_FILEFORMAT_MOD) - case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music->ctxData); break; + case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music.ctxData); break; #endif default: break; } - music->sampleLeft = music->sampleCount; + music.sampleLeft = music.sampleCount; } // Update (re-fill) music buffers if data already processed void UpdateMusicStream(Music music) { - if (music == NULL) return; - bool streamEnding = false; - unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.buffer)->bufferSizeInFrames/2; + unsigned int subBufferSizeInFrames = music.stream.buffer->bufferSizeInFrames/2; // NOTE: Using dynamic allocation because it could require more than 16KB - void *pcm = RL_CALLOC(subBufferSizeInFrames*music->stream.channels*music->stream.sampleSize/8, 1); + void *pcm = RL_CALLOC(subBufferSizeInFrames*music.stream.channels*music.stream.sampleSize/8, 1); int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts - while (IsAudioBufferProcessed(music->stream)) + while (IsAudioBufferProcessed(music.stream)) { - if ((music->sampleLeft/music->stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music->stream.channels; - else samplesCount = music->sampleLeft; + if ((music.sampleLeft/music.stream.channels) >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames*music.stream.channels; + else samplesCount = music.sampleLeft; - switch (music->ctxType) + switch (music.ctxType) { #if defined(SUPPORT_FILEFORMAT_OGG) case MUSIC_AUDIO_OGG: { // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) - stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music->ctxData, music->stream.channels, (short *)pcm, samplesCount); + stb_vorbis_get_samples_short_interleaved((stb_vorbis *)music.ctxData, music.stream.channels, (short *)pcm, samplesCount); } break; #endif @@ -1499,7 +1437,7 @@ void UpdateMusicStream(Music music) case MUSIC_AUDIO_FLAC: { // NOTE: Returns the number of samples to process (not required) - drflac_read_s16((drflac *)music->ctxData, samplesCount, (short *)pcm); + drflac_read_s16((drflac *)music.ctxData, samplesCount, (short *)pcm); } break; #endif @@ -1507,7 +1445,7 @@ void UpdateMusicStream(Music music) case MUSIC_AUDIO_MP3: { // NOTE: samplesCount, actually refers to framesCount and returns the number of frames processed - drmp3_read_pcm_frames_f32((drmp3 *)music->ctxData, samplesCount/music->stream.channels, (float *)pcm); + drmp3_read_pcm_frames_f32((drmp3 *)music.ctxData, samplesCount/music.stream.channels, (float *)pcm); } break; #endif @@ -1515,29 +1453,29 @@ void UpdateMusicStream(Music music) case MUSIC_MODULE_XM: { // NOTE: Internally this function considers 2 channels generation, so samplesCount/2 - jar_xm_generate_samples_16bit((jar_xm_context_t *)music->ctxData, (short *)pcm, samplesCount/2); + jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)pcm, samplesCount/2); } break; #endif #if defined(SUPPORT_FILEFORMAT_MOD) case MUSIC_MODULE_MOD: { // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2 - jar_mod_fillbuffer((jar_mod_context_t *)music->ctxData, (short *)pcm, samplesCount/2, 0); + jar_mod_fillbuffer((jar_mod_context_t *)music.ctxData, (short *)pcm, samplesCount/2, 0); } break; #endif default: break; } - UpdateAudioStream(music->stream, pcm, samplesCount); + UpdateAudioStream(music.stream, pcm, samplesCount); - if ((music->ctxType == MUSIC_MODULE_XM) || (music->ctxType == MUSIC_MODULE_MOD)) + if ((music.ctxType == MUSIC_MODULE_XM) || (music.ctxType == MUSIC_MODULE_MOD)) { - if (samplesCount > 1) music->sampleLeft -= samplesCount/2; - else music->sampleLeft -= samplesCount; + if (samplesCount > 1) music.sampleLeft -= samplesCount/2; + else music.sampleLeft -= samplesCount; } - else music->sampleLeft -= samplesCount; + else music.sampleLeft -= samplesCount; - if (music->sampleLeft <= 0) + if (music.sampleLeft <= 0) { streamEnding = true; break; @@ -1553,14 +1491,14 @@ void UpdateMusicStream(Music music) StopMusicStream(music); // Stop music (and reset) // Decrease loopCount to stop when required - if (music->loopCount > 1) + if (music.loopCount > 1) { - music->loopCount--; // Decrease loop count + music.loopCount--; // Decrease loop count PlayMusicStream(music); // Play again } else { - if (music->loopCount == 0) PlayMusicStream(music); + if (music.loopCount == 0) PlayMusicStream(music); } } else @@ -1574,27 +1512,26 @@ void UpdateMusicStream(Music music) // Check if any music is playing bool IsMusicPlaying(Music music) { - if (music == NULL) return false; - else return IsAudioStreamPlaying(music->stream); + return IsAudioStreamPlaying(music.stream); } // Set volume for music void SetMusicVolume(Music music, float volume) { - if (music != NULL) SetAudioStreamVolume(music->stream, volume); + SetAudioStreamVolume(music.stream, volume); } // Set pitch for music void SetMusicPitch(Music music, float pitch) { - if (music != NULL) SetAudioStreamPitch(music->stream, pitch); + SetAudioStreamPitch(music.stream, pitch); } // Set music loop count (loop repeats) // NOTE: If set to -1, means infinite loop void SetMusicLoopCount(Music music, int count) { - if (music != NULL) music->loopCount = count; + music.loopCount = count; } // Get music time length (in seconds) @@ -1602,7 +1539,7 @@ float GetMusicTimeLength(Music music) { float totalSeconds = 0.0f; - if (music != NULL) totalSeconds = (float)music->sampleCount/(music->stream.sampleRate*music->stream.channels); + totalSeconds = (float)music.sampleCount/(music.stream.sampleRate*music.stream.channels); return totalSeconds; } @@ -1612,11 +1549,8 @@ float GetMusicTimePlayed(Music music) { float secondsPlayed = 0.0f; - if (music != NULL) - { - unsigned int samplesPlayed = music->sampleCount - music->sampleLeft; - secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels); - } + unsigned int samplesPlayed = music.sampleCount - music.sampleLeft; + secondsPlayed = (float)samplesPlayed/(music.stream.sampleRate*music.stream.channels); return secondsPlayed; } diff --git a/src/raudio.h b/src/raudio.h index e032d09bc..8bbbe8613 100644 --- a/src/raudio.h +++ b/src/raudio.h @@ -80,40 +80,44 @@ // Wave type, defines audio wave data typedef struct Wave { - unsigned int sampleCount; // Number of samples - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo) - void *data; // Buffer data pointer + unsigned int sampleCount; // Total number of samples + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo) + void *data; // Buffer data pointer } Wave; -// Sound source type -typedef struct Sound { - void *audioBuffer; // Pointer to internal data used by the audio system - - unsigned int source; // Audio source id - unsigned int buffer; // Audio buffer id - int format; // Audio format specifier -} Sound; - -// Music type (file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed -typedef struct MusicData *Music; +typedef struct rAudioBuffer rAudioBuffer; // Audio stream type // NOTE: Useful to create custom audio streams not bound to a specific file typedef struct AudioStream { - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo) + unsigned int sampleRate; // Frequency (samples per second) + unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels (1-mono, 2-stereo) - void *audioBuffer; // Pointer to internal data used by the audio system. - - int format; // Audio format specifier - unsigned int source; // Audio source id - unsigned int buffers[2]; // Audio buffers (double buffering) + rAudioBuffer *buffer; // Pointer to internal data used by the audio system } AudioStream; +// Sound source type +typedef struct Sound { + unsigned int sampleCount; // Total number of samples + AudioStream stream; // Audio stream +} Sound; + +// Music stream type (audio file streaming from memory) +// NOTE: Anything longer than ~10 seconds should be streamed +typedef struct Music { + int ctxType; // Type of music context (audio filetype) + void *ctxData; // Audio context data, depends on type + + unsigned int sampleCount; // Total number of samples + unsigned int sampleLeft; // Number of samples left to end + unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop + + AudioStream stream; // Audio stream +} Music; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -126,25 +130,31 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- + +// Audio device management functions void InitAudioDevice(void); // Initialize audio device and context void CloseAudioDevice(void); // Close the audio device and context bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully void SetMasterVolume(float volume); // Set master volume (listener) +// Wave/Sound loading/unloading functions Wave LoadWave(const char *fileName); // Load wave data from file -Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data Sound LoadSound(const char *fileName); // Load sound from file Sound LoadSoundFromWave(Wave wave); // Load sound from wave data void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data void UnloadWave(Wave wave); // Unload wave data void UnloadSound(Sound sound); // Unload sound +void ExportWave(Wave wave, const char *fileName); // Export wave data to file +void ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h) + +// Wave/Sound management functions void PlaySound(Sound sound); // Play a sound -void PlaySoundMulti(Sound sound); // Play a sound using the multi channel buffer pool -int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel buffer pool +void StopSound(Sound sound); // Stop playing a sound void PauseSound(Sound sound); // Pause a sound void ResumeSound(Sound sound); // Resume a paused sound -void StopSound(Sound sound); // Stop playing a sound -void StopSoundMulti(void); // Stop any sound played with PlaySoundMulti() +void PlaySoundMulti(Sound sound); // Play a sound (using multichannel buffer pool) +void StopSoundMulti(void); // Stop any sound playing (using multichannel buffer pool) +int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) @@ -152,6 +162,8 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // C Wave WaveCopy(Wave wave); // Copy a wave to a new wave void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range float *GetWaveData(Wave wave); // Get samples data from wave as a floats array + +// Music management functions Music LoadMusicStream(const char *fileName); // Load music stream from file void UnloadMusicStream(Music music); // Unload music stream void PlayMusicStream(Music music); // Start music playing @@ -167,9 +179,7 @@ float GetMusicTimeLength(Music music); // Get music tim float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) // AudioStream management functions -AudioStream InitAudioStream(unsigned int sampleRate, - unsigned int sampleSize, - unsigned int channels); // Init audio stream (to stream raw audio pcm data) +AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Init audio stream (to stream raw audio pcm data) void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // Update audio stream buffers with data void CloseAudioStream(AudioStream stream); // Close audio stream and free memory bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill diff --git a/src/raylib.h b/src/raylib.h index cd75f1fe5..ec5dee83f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -114,40 +114,40 @@ // NOTE: MSC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized from { } initializers. #if defined(__cplusplus) - #define CLITERAL + #define CLITERAL(type) type #else - #define CLITERAL (Color) + #define CLITERAL(type) (type) #endif // Some Basic Colors // NOTE: Custom raylib color palette for amazing visuals on WHITE background -#define LIGHTGRAY CLITERAL{ 200, 200, 200, 255 } // Light Gray -#define GRAY CLITERAL{ 130, 130, 130, 255 } // Gray -#define DARKGRAY CLITERAL{ 80, 80, 80, 255 } // Dark Gray -#define YELLOW CLITERAL{ 253, 249, 0, 255 } // Yellow -#define GOLD CLITERAL{ 255, 203, 0, 255 } // Gold -#define ORANGE CLITERAL{ 255, 161, 0, 255 } // Orange -#define PINK CLITERAL{ 255, 109, 194, 255 } // Pink -#define RED CLITERAL{ 230, 41, 55, 255 } // Red -#define MAROON CLITERAL{ 190, 33, 55, 255 } // Maroon -#define GREEN CLITERAL{ 0, 228, 48, 255 } // Green -#define LIME CLITERAL{ 0, 158, 47, 255 } // Lime -#define DARKGREEN CLITERAL{ 0, 117, 44, 255 } // Dark Green -#define SKYBLUE CLITERAL{ 102, 191, 255, 255 } // Sky Blue -#define BLUE CLITERAL{ 0, 121, 241, 255 } // Blue -#define DARKBLUE CLITERAL{ 0, 82, 172, 255 } // Dark Blue -#define PURPLE CLITERAL{ 200, 122, 255, 255 } // Purple -#define VIOLET CLITERAL{ 135, 60, 190, 255 } // Violet -#define DARKPURPLE CLITERAL{ 112, 31, 126, 255 } // Dark Purple -#define BEIGE CLITERAL{ 211, 176, 131, 255 } // Beige -#define BROWN CLITERAL{ 127, 106, 79, 255 } // Brown -#define DARKBROWN CLITERAL{ 76, 63, 47, 255 } // Dark Brown +#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray +#define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray +#define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray +#define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow +#define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold +#define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange +#define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink +#define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red +#define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon +#define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green +#define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime +#define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green +#define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue +#define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue +#define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue +#define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple +#define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet +#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple +#define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige +#define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown +#define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown -#define WHITE CLITERAL{ 255, 255, 255, 255 } // White -#define BLACK CLITERAL{ 0, 0, 0, 255 } // Black -#define BLANK CLITERAL{ 0, 0, 0, 0 } // Blank (Transparent) -#define MAGENTA CLITERAL{ 255, 0, 255, 255 } // Magenta -#define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo) +#define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White +#define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black +#define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent) +#define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta +#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo) // Temporal hack to avoid breaking old codebases using // deprecated raylib implementation of these functions @@ -433,7 +433,7 @@ typedef struct Sound { // Music stream type (audio file streaming from memory) // NOTE: Anything longer than ~10 seconds should be streamed -typedef struct MusicStream { +typedef struct Music { int ctxType; // Type of music context (audio filetype) void *ctxData; // Audio context data, depends on type @@ -442,7 +442,7 @@ typedef struct MusicStream { unsigned int loopCount; // Loops count (times music will play), 0 means infinite loop AudioStream stream; // Audio stream -} MusicStream, *Music; +} Music; // Head-Mounted-Display device parameters typedef struct VrDeviceInfo { @@ -1350,7 +1350,6 @@ RLAPI void SetMasterVolume(float volume); // Set mas // Wave/Sound loading/unloading functions RLAPI Wave LoadWave(const char *fileName); // Load wave data from file -RLAPI Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data @@ -1361,12 +1360,12 @@ RLAPI void ExportWaveAsCode(Wave wave, const char *fileName); // Export // Wave/Sound management functions RLAPI void PlaySound(Sound sound); // Play a sound -RLAPI void PlaySoundMulti(Sound sound); // Play a sound using the multi channel buffer pool -RLAPI int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel buffer pool +RLAPI void StopSound(Sound sound); // Stop playing a sound RLAPI void PauseSound(Sound sound); // Pause a sound RLAPI void ResumeSound(Sound sound); // Resume a paused sound -RLAPI void StopSound(Sound sound); // Stop playing a sound -RLAPI void StopSoundMulti(void); // Stop any sound played with PlaySoundMulti() +RLAPI void PlaySoundMulti(Sound sound); // Play a sound (using multichannel buffer pool) +RLAPI void StopSoundMulti(void); // Stop any sound playing (using multichannel buffer pool) +RLAPI int GetSoundsPlaying(void); // Get number of sounds playing in the multichannel RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) diff --git a/src/rlgl.h b/src/rlgl.h index d749f7d91..797ea9c06 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -735,7 +735,7 @@ typedef struct DrawCall { int mode; // Drawing mode: LINES, TRIANGLES, QUADS int vertexCount; // Number of vertex of the draw int vertexAlignment; // Number of vertex required for index alignment (LINES, TRIANGLES) - //unsigned int vaoId; // Vertex Array id to be used on the draw + //unsigned int vaoId; // Vertex array id to be used on the draw //unsigned int shaderId; // Shader id to be used on the draw unsigned int textureId; // Texture id to be used on the draw // TODO: Support additional texture units? @@ -1140,8 +1140,8 @@ void rlEnd(void) { // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! - // Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw - rlPopMatrix(); + // If we have multiple matrix pushed, it will require "stackCounter" pops before launching the draw + for (int i = stackCounter; i >= 0; i--) rlPopMatrix(); rlglDraw(); } } @@ -1291,11 +1291,11 @@ void rlTextureParameters(unsigned int id, int param, int value) { if (value == RL_WRAP_MIRROR_CLAMP) { -#if !defined(GRAPHICS_API_OPENGL_11) - if (!texMirrorClampSupported) TraceLog(LOG_WARNING, "Clamp mirror wrap mode not supported"); -#endif + if (texMirrorClampSupported) glTexParameteri(GL_TEXTURE_2D, param, value); + else TraceLog(LOG_WARNING, "Clamp mirror wrap mode not supported"); } else glTexParameteri(GL_TEXTURE_2D, param, value); + } break; case RL_TEXTURE_MAG_FILTER: case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; @@ -2622,9 +2622,17 @@ void rlDrawMesh(Mesh mesh, Material material, Matrix transform) // That's because BeginMode3D() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() Matrix matView = modelview; // View matrix (camera) Matrix matProjection = projection; // Projection matrix (perspective) + + // TODO: Matrix nightmare! Trying to combine stack matrices with view matrix and local model transform matrix.. + // There is some problem in the order matrices are multiplied... it requires some time to figure out... + Matrix matStackTransform = MatrixIdentity(); + + // TODO: Consider possible transform matrices in the stack + // Is this the right order? or should we start with the first stored matrix instead of the last one? + //for (int i = stackCounter; i > 0; i--) matStackTransform = MatrixMultiply(stack[i], matStackTransform); - // Calculate model-view matrix combining matModel and matView - Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates + Matrix matModel = MatrixMultiply(transform, matStackTransform); // Apply local model transformation + Matrix matModelView = MatrixMultiply(matModel, matView); // Transform to camera-space coordinates //----------------------------------------------------- // Bind active texture maps (if available) diff --git a/src/rmem.h b/src/rmem.h index 65e081948..87ceacc23 100644 --- a/src/rmem.h +++ b/src/rmem.h @@ -77,9 +77,13 @@ typedef struct Stack { size_t size; } Stack; +#define MEMPOOL_BUCKET_SIZE 8 +#define MEMPOOL_BUCKET_BITS 3 + typedef struct MemPool { AllocList freeList; Stack stack; + MemNode *buckets[MEMPOOL_BUCKET_SIZE]; } MemPool; // Object Pool @@ -164,10 +168,19 @@ static inline size_t __AlignSize(const size_t size, const size_t align) return (size + (align - 1)) & -align; } -static void __RemoveNode(MemNode **const node) +static void __RemoveNode(MemPool *const mempool, MemNode **const node) { - ((*node)->prev != NULL)? ((*node)->prev->next = (*node)->next) : (*node = (*node)->next); - ((*node)->next != NULL)? ((*node)->next->prev = (*node)->prev) : (*node = (*node)->prev); + if ((*node)->next != NULL) (*node)->next->prev = (*node)->prev; + else { + mempool->freeList.tail = (*node)->prev; + if (mempool->freeList.tail != NULL) mempool->freeList.tail->next = NULL; + } + + if ((*node)->prev != NULL) (*node)->prev->next = (*node)->next; + else { + mempool->freeList.head = (*node)->next; + if (mempool->freeList.head != NULL) mempool->freeList.head->prev = NULL; + } } //---------------------------------------------------------------------------------- @@ -183,7 +196,7 @@ MemPool CreateMemPool(const size_t size) { // Align the mempool size to at least the size of an alloc node. mempool.stack.size = size; - mempool.stack.mem = malloc(1 + mempool.stack.size*sizeof *mempool.stack.mem); + mempool.stack.mem = malloc(mempool.stack.size*sizeof *mempool.stack.mem); if (mempool.stack.mem==NULL) { @@ -229,8 +242,16 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) { MemNode *new_mem = NULL; const size_t ALLOC_SIZE = __AlignSize(size + sizeof *new_mem, sizeof(intptr_t)); + const size_t BUCKET_INDEX = (ALLOC_SIZE >> MEMPOOL_BUCKET_BITS) - 1; - if (mempool->freeList.head != NULL) + if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE && mempool->buckets[BUCKET_INDEX] != NULL && mempool->buckets[BUCKET_INDEX]->size >= ALLOC_SIZE) + { + new_mem = mempool->buckets[BUCKET_INDEX]; + mempool->buckets[BUCKET_INDEX] = mempool->buckets[BUCKET_INDEX]->next; + if( mempool->buckets[BUCKET_INDEX] != NULL ) + mempool->buckets[BUCKET_INDEX]->prev = NULL; + } + else if (mempool->freeList.head != NULL) { const size_t MEM_SPLIT_THRESHOLD = 16; @@ -242,9 +263,8 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) { // Close in size - reduce fragmentation by not splitting. new_mem = *inode; - __RemoveNode(inode); + __RemoveNode(mempool, inode); mempool->freeList.len--; - new_mem->next = new_mem->prev = NULL; break; } else @@ -253,7 +273,6 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) new_mem = (MemNode *)((uint8_t *)*inode + ((*inode)->size - ALLOC_SIZE)); (*inode)->size -= ALLOC_SIZE; new_mem->size = ALLOC_SIZE; - new_mem->next = new_mem->prev = NULL; break; } } @@ -272,19 +291,20 @@ void *MemPoolAlloc(MemPool *const mempool, const size_t size) // Use the available mempool space as the new node. new_mem = (MemNode *)mempool->stack.base; new_mem->size = ALLOC_SIZE; - new_mem->next = new_mem->prev = NULL; } } // Visual of the allocation block. // -------------- // | mem size | lowest addr of block - // | next node | + // | next node | 12 byte (32-bit) header + // | prev node | 24 byte (64-bit) header // -------------- // | alloc'd | // | memory | // | space | highest addr of block // -------------- + new_mem->next = new_mem->prev = NULL; uint8_t *const final_mem = (uint8_t *)new_mem + sizeof *new_mem; memset(final_mem, 0, new_mem->size - sizeof *new_mem); return final_mem; @@ -296,17 +316,17 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si if ((mempool == NULL) || (size > mempool->stack.size)) return NULL; // NULL ptr should make this work like regular Allocation. else if (ptr == NULL) return MemPoolAlloc(mempool, size); - else if ((uintptr_t)ptr <= (uintptr_t)mempool->stack.mem) return NULL; + else if ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem) return NULL; else { - MemNode *node = (MemNode *)((uint8_t *)ptr - sizeof *node); + MemNode *const node = (MemNode *)((uint8_t *)ptr - sizeof *node); const size_t NODE_SIZE = sizeof *node; - uint8_t *resized_block = MemPoolAlloc(mempool, size); + uint8_t *const resized_block = MemPoolAlloc(mempool, size); if (resized_block == NULL) return NULL; else { - MemNode *resized = (MemNode *)(resized_block - sizeof *resized); + MemNode *const resized = (MemNode *)(resized_block - sizeof *resized); memmove(resized_block, ptr, (node->size > resized->size)? (resized->size - NODE_SIZE) : (node->size - NODE_SIZE)); MemPoolFree(mempool, ptr); return resized_block; @@ -316,11 +336,12 @@ void *MemPoolRealloc(MemPool *const restrict mempool, void *ptr, const size_t si void MemPoolFree(MemPool *const restrict mempool, void *ptr) { - if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr <= (uintptr_t)mempool->stack.mem)) return; + if ((mempool == NULL) || (ptr == NULL) || ((uintptr_t)ptr - sizeof(MemNode) < (uintptr_t)mempool->stack.mem)) return; else { // Behind the actual pointer data is the allocation info. - MemNode *mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node); + MemNode *const mem_node = (MemNode *)((uint8_t *)ptr - sizeof *mem_node); + const size_t BUCKET_INDEX = (mem_node->size >> MEMPOOL_BUCKET_BITS) - 1; // Make sure the pointer data is valid. if (((uintptr_t)mem_node < (uintptr_t)mempool->stack.base) || @@ -332,51 +353,43 @@ void MemPoolFree(MemPool *const restrict mempool, void *ptr) { mempool->stack.base += mem_node->size; } + // attempted stack merge failed, try to place it into the memnode buckets + else if (BUCKET_INDEX < MEMPOOL_BUCKET_SIZE) + { + if (mempool->buckets[index] == NULL) mempool->buckets[index] = node; + else + { + for (MemNode *n = mempool->buckets[index]; n != NULL; n = n->next) if( n==node ) return; + mempool->buckets[index]->prev = node; + node->next = mempool->buckets[index]; + mempool->buckets[index] = node; + } + } // Otherwise, we add it to the free list. // We also check if the freelist already has the pointer so we can prevent double frees. - else if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size)) + else /*if ((mempool->freeList.len == 0UL) || ((uintptr_t)mempool->freeList.head >= (uintptr_t)mempool->stack.mem && (uintptr_t)mempool->freeList.head - (uintptr_t)mempool->stack.mem < mempool->stack.size))*/ { for (MemNode *n = mempool->freeList.head; n != NULL; n = n->next) if (n == mem_node) return; - // This code inserts at head. - /* - ( mempool->freeList.head==NULL)? (mempool->freeList.tail = mem_node) : (mempool->freeList.head->prev = mem_node); - mem_node->next = mempool->freeList.head; - mempool->freeList.head = mem_node; - mempool->freeList.len++; - */ - - // This code insertion sorts where largest size is first. + // This code insertion sorts where largest size is last. if (mempool->freeList.head == NULL) { mempool->freeList.head = mempool->freeList.tail = mem_node; mempool->freeList.len++; } - else if (mempool->freeList.head->size <= mem_node->size) + else if (mempool->freeList.head->size >= mem_node->size) { mem_node->next = mempool->freeList.head; mem_node->next->prev = mem_node; mempool->freeList.head = mem_node; mempool->freeList.len++; } - else if (mempool->freeList.tail->size > mem_node->size) + else //if (mempool->freeList.tail->size <= mem_node->size) { mem_node->prev = mempool->freeList.tail; mempool->freeList.tail->next = mem_node; mempool->freeList.tail = mem_node; mempool->freeList.len++; - } - else - { - MemNode *n = mempool->freeList.head; - while ((n->next != NULL) && (n->next->size > mem_node->size)) n = n->next; - - mem_node->next = n->next; - if (n->next != NULL) mem_node->next->prev = mem_node; - - n->next = mem_node; - mem_node->prev = n; - mempool->freeList.len++; } if (mempool->freeList.autoDefrag && (mempool->freeList.maxNodes != 0UL) && (mempool->freeList.len > mempool->freeList.maxNodes)) MemPoolDefrag(mempool); @@ -400,6 +413,8 @@ size_t GetMemPoolFreeMemory(const MemPool mempool) for (MemNode *n=mempool.freeList.head; n != NULL; n = n->next) total_remaining += n->size; + for (size_t i=0; inext) total_remaining += n->size; + return total_remaining; } @@ -411,12 +426,29 @@ bool MemPoolDefrag(MemPool *const mempool) // If the memory pool has been entirely released, fully defrag it. if (mempool->stack.size == GetMemPoolFreeMemory(*mempool)) { - memset(&mempool->freeList, 0, sizeof mempool->freeList); + mempool->freeList.head = mempool->freeList.tail = NULL; + mempool->freeList.len = 0; + for (size_t i = 0; i < MEMPOOL_BUCKET_SIZE; i++) mempool->buckets[i] = NULL; mempool->stack.base = mempool->stack.mem + mempool->stack.size; return true; } else { + for (size_t i=0; ibuckets[i] != NULL) + { + if ((uintptr_t)mempool->buckets[i] == (uintptr_t)mempool->stack.base) + { + mempool->stack.base += mempool->buckets[i]->size; + mempool->buckets[i]->size = 0; + mempool->buckets[i] = mempool->buckets[i]->next; + if (mempool->buckets[i] != NULL) mempool->buckets[i]->prev = NULL; + } + else break; + } + } + const size_t PRE_DEFRAG_LEN = mempool->freeList.len; MemNode **node = &mempool->freeList.head; @@ -427,7 +459,7 @@ bool MemPoolDefrag(MemPool *const mempool) // If node is right at the stack, merge it back into the stack. mempool->stack.base += (*node)->size; (*node)->size = 0UL; - __RemoveNode(node); + __RemoveNode(mempool, node); mempool->freeList.len--; node = &mempool->freeList.head; } @@ -475,6 +507,7 @@ bool MemPoolDefrag(MemPool *const mempool) (*node)->size = 0UL; (*node)->next->prev = (*node)->prev; (*node)->prev->next = (*node)->next; + *node = (*node)->next; mempool->freeList.len--; node = &mempool->freeList.head; @@ -487,6 +520,7 @@ bool MemPoolDefrag(MemPool *const mempool) (*node)->size = 0UL; (*node)->next->prev = (*node)->prev; (*node)->prev->next = (*node)->next; + *node = (*node)->prev; mempool->freeList.len--; node = &mempool->freeList.head; @@ -513,7 +547,7 @@ void ToggleMemPoolAutoDefrag(MemPool *const mempool) //---------------------------------------------------------------------------------- union ObjInfo { uint8_t *const byte; - size_t *const size; + size_t *const index; }; ObjPool CreateObjPool(const size_t objsize, const size_t len) @@ -537,7 +571,7 @@ ObjPool CreateObjPool(const size_t objsize, const size_t len) for (size_t i=0; istack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.size*objpool->objSize) : NULL; + objpool->stack.base = (objpool->freeBlocks != 0UL)? objpool->stack.mem + (*ret.index*objpool->objSize) : NULL; memset(ret.byte, 0, objpool->objSize); return ret.byte; } @@ -611,7 +645,7 @@ void ObjPoolFree(ObjPool *const restrict objpool, void *ptr) // When we free our pointer, we recycle the pointer space to store the previous index and then we push it as our new head. // *p = index of Head in relation to the buffer; // Head = p; - *p.size = (objpool->stack.base != NULL)? (objpool->stack.base - objpool->stack.mem)/objpool->objSize : objpool->stack.size; + *p.index = (objpool->stack.base != NULL)? (objpool->stack.base - objpool->stack.mem)/objpool->objSize : objpool->stack.size; objpool->stack.base = p.byte; objpool->freeBlocks++; } diff --git a/templates/advance_game/Makefile b/templates/advance_game/Makefile index 95fb6f6f2..6cd7d66a1 100644 --- a/templates/advance_game/Makefile +++ b/templates/advance_game/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/templates/simple_game/Makefile b/templates/simple_game/Makefile index ea732c928..326f4b7cc 100644 --- a/templates/simple_game/Makefile +++ b/templates/simple_game/Makefile @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif diff --git a/templates/standard_game/Makefile b/templates/standard_game/Makefile index 56b76b424..b06bf3201 100644 --- a/templates/standard_game/Makefile +++ b/templates/standard_game/Makefile @@ -230,7 +230,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif # Define a custom shell .html and output extension - CFLAGS += --shell-file $(RAYLIB_PATH)\src\shell.html + CFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif