From 62f1511e5f36fdf4e9dfb5b7d930c3121bf7072c Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 May 2019 16:00:16 +0200 Subject: [PATCH] Batch of 9 new shapes examples! Some examples included in this batch require the included libraries: `easings.h` and `raygui.h`. Examples included: - shapes_bouncing_ball - shapes_collision_area - shapes_following_eyes - shapes_draw_circle_sector (requires raygui.h) - shapes_draw_rectangle_rounded (requires raygui.h) - shapes_draw_ring (requires raygui.h) - shapes_easings_ball_anim (requires easings.h) - shapes_easings_box_anim (requires easings.h) - shapes_easings_rectangle_array (requires easings.h) --- examples/shapes/easings.h | 257 ++ examples/shapes/raygui.h | 3306 +++++++++++++++++ examples/shapes/shapes_bouncing_ball.c | 76 + examples/shapes/shapes_bouncing_ball.png | Bin 0 -> 15523 bytes examples/shapes/shapes_collision_area.c | 108 + examples/shapes/shapes_collision_area.png | Bin 0 -> 15319 bytes examples/shapes/shapes_draw_circle_sector.c | 79 + examples/shapes/shapes_draw_circle_sector.png | Bin 0 -> 18474 bytes .../shapes/shapes_draw_rectangle_rounded.c | 87 + .../shapes/shapes_draw_rectangle_rounded.png | Bin 0 -> 17952 bytes examples/shapes/shapes_draw_ring.c | 92 + examples/shapes/shapes_draw_ring.png | Bin 0 -> 21873 bytes examples/shapes/shapes_easing_box_anim.c | 136 + examples/shapes/shapes_easing_box_anim.png | Bin 0 -> 15217 bytes examples/shapes/shapes_easings_ball_anim.c | 110 + examples/shapes/shapes_easings_ball_anim.png | Bin 0 -> 15602 bytes .../shapes/shapes_easings_rectangle_array.c | 118 + .../shapes/shapes_easings_rectangle_array.png | Bin 0 -> 17142 bytes examples/shapes/shapes_following_eyes.c | 104 + examples/shapes/shapes_following_eyes.png | Bin 0 -> 16169 bytes 20 files changed, 4473 insertions(+) create mode 100644 examples/shapes/easings.h create mode 100644 examples/shapes/raygui.h create mode 100644 examples/shapes/shapes_bouncing_ball.c create mode 100644 examples/shapes/shapes_bouncing_ball.png create mode 100644 examples/shapes/shapes_collision_area.c create mode 100644 examples/shapes/shapes_collision_area.png create mode 100644 examples/shapes/shapes_draw_circle_sector.c create mode 100644 examples/shapes/shapes_draw_circle_sector.png create mode 100644 examples/shapes/shapes_draw_rectangle_rounded.c create mode 100644 examples/shapes/shapes_draw_rectangle_rounded.png create mode 100644 examples/shapes/shapes_draw_ring.c create mode 100644 examples/shapes/shapes_draw_ring.png create mode 100644 examples/shapes/shapes_easing_box_anim.c create mode 100644 examples/shapes/shapes_easing_box_anim.png create mode 100644 examples/shapes/shapes_easings_ball_anim.c create mode 100644 examples/shapes/shapes_easings_ball_anim.png create mode 100644 examples/shapes/shapes_easings_rectangle_array.c create mode 100644 examples/shapes/shapes_easings_rectangle_array.png create mode 100644 examples/shapes/shapes_following_eyes.c create mode 100644 examples/shapes/shapes_following_eyes.png diff --git a/examples/shapes/easings.h b/examples/shapes/easings.h new file mode 100644 index 000000000..810aeccb6 --- /dev/null +++ b/examples/shapes/easings.h @@ -0,0 +1,257 @@ +/******************************************************************************************* +* +* raylib easings (header only file) +* +* Useful easing functions for values animation +* +* This header uses: +* #define EASINGS_STATIC_INLINE // Inlines all functions code, so it runs faster. +* // This requires lots of memory on system. +* How to use: +* The four inputs t,b,c,d are defined as follows: +* t = current time (in any unit measure, but same unit as duration) +* b = starting value to interpolate +* c = the total change in value of b that needs to occur +* d = total time it should take to complete (duration) +* +* Example: +* +* int currentTime = 0; +* int duration = 100; +* float startPositionX = 0.0f; +* float finalPositionX = 30.0f; +* float currentPositionX = startPositionX; +* +* while (currentPositionX < finalPositionX) +* { +* currentPositionX = EaseSineIn(currentTime, startPositionX, finalPositionX - startPositionX, duration); +* currentTime++; +* } +* +* A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/) +* +* Robert Penner License +* --------------------------------------------------------------------------------- +* Open source under the BSD License. +* +* Copyright (c) 2001 Robert Penner. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* - Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* - Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* - Neither the name of the author nor the names of contributors may be used +* to endorse or promote products derived from this software without specific +* prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +* OF THE POSSIBILITY OF SUCH DAMAGE. +* --------------------------------------------------------------------------------- +* +* Copyright (c) 2015 Ramon Santamaria +* +* 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. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef EASINGS_H +#define EASINGS_H + +#define EASINGS_STATIC_INLINE // NOTE: By default, compile functions as static inline + +#if defined(EASINGS_STATIC_INLINE) + #define EASEDEF static inline +#else + #define EASEDEF extern +#endif + +#include // Required for: sin(), cos(), sqrt(), pow() + +#ifndef PI + #define PI 3.14159265358979323846f //Required as PI is not always defined in math.h +#endif + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +// Linear Easing functions +EASEDEF float EaseLinearNone(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearIn(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearOut(float t, float b, float c, float d) { return (c*t/d + b); } +EASEDEF float EaseLinearInOut(float t,float b, float c, float d) { return (c*t/d + b); } + +// Sine Easing functions +EASEDEF float EaseSineIn(float t, float b, float c, float d) { return (-c*cos(t/d*(PI/2)) + c + b); } +EASEDEF float EaseSineOut(float t, float b, float c, float d) { return (c*sin(t/d*(PI/2)) + b); } +EASEDEF float EaseSineInOut(float t, float b, float c, float d) { return (-c/2*(cos(PI*t/d) - 1) + b); } + +// Circular Easing functions +EASEDEF float EaseCircIn(float t, float b, float c, float d) { return (-c*(sqrt(1 - (t/=d)*t) - 1) + b); } +EASEDEF float EaseCircOut(float t, float b, float c, float d) { return (c*sqrt(1 - (t=t/d-1)*t) + b); } +EASEDEF float EaseCircInOut(float t, float b, float c, float d) +{ + if ((t/=d/2) < 1) return (-c/2*(sqrt(1 - t*t) - 1) + b); + return (c/2*(sqrt(1 - t*(t-=2)) + 1) + b); +} + +// Cubic Easing functions +EASEDEF float EaseCubicIn(float t, float b, float c, float d) { return (c*(t/=d)*t*t + b); } +EASEDEF float EaseCubicOut(float t, float b, float c, float d) { return (c*((t=t/d-1)*t*t + 1) + b); } +EASEDEF float EaseCubicInOut(float t, float b, float c, float d) +{ + if ((t/=d/2) < 1) return (c/2*t*t*t + b); + return (c/2*((t-=2)*t*t + 2) + b); +} + +// Quadratic Easing functions +EASEDEF float EaseQuadIn(float t, float b, float c, float d) { return (c*(t/=d)*t + b); } +EASEDEF float EaseQuadOut(float t, float b, float c, float d) { return (-c*(t/=d)*(t-2) + b); } +EASEDEF float EaseQuadInOut(float t, float b, float c, float d) +{ + if ((t/=d/2) < 1) return (((c/2)*(t*t)) + b); + return (-c/2*(((t-2)*(--t)) - 1) + b); +} + +// Exponential Easing functions +EASEDEF float EaseExpoIn(float t, float b, float c, float d) { return (t == 0) ? b : (c*pow(2, 10*(t/d - 1)) + b); } +EASEDEF float EaseExpoOut(float t, float b, float c, float d) { return (t == d) ? (b + c) : (c*(-pow(2, -10*t/d) + 1) + b); } +EASEDEF float EaseExpoInOut(float t, float b, float c, float d) +{ + if (t == 0) return b; + if (t == d) return (b + c); + if ((t/=d/2) < 1) return (c/2*pow(2, 10*(t - 1)) + b); + + return (c/2*(-pow(2, -10*--t) + 2) + b); +} + +// Back Easing functions +EASEDEF float EaseBackIn(float t, float b, float c, float d) +{ + float s = 1.70158f; + float postFix = t/=d; + return (c*(postFix)*t*((s + 1)*t - s) + b); +} + +EASEDEF float EaseBackOut(float t, float b, float c, float d) +{ + float s = 1.70158f; + return (c*((t=t/d-1)*t*((s + 1)*t + s) + 1) + b); +} + +EASEDEF float EaseBackInOut(float t, float b, float c, float d) +{ + float s = 1.70158f; + if ((t/=d/2) < 1) return (c/2*(t*t*(((s*=(1.525f)) + 1)*t - s)) + b); + + float postFix = t-=2; + return (c/2*((postFix)*t*(((s*=(1.525f)) + 1)*t + s) + 2) + b); +} + +// Bounce Easing functions +EASEDEF float EaseBounceOut(float t, float b, float c, float d) +{ + if ((t/=d) < (1/2.75f)) + { + return (c*(7.5625f*t*t) + b); + } + else if (t < (2/2.75f)) + { + float postFix = t-=(1.5f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.75f) + b); + } + else if (t < (2.5/2.75)) + { + float postFix = t-=(2.25f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.9375f) + b); + } + else + { + float postFix = t-=(2.625f/2.75f); + return (c*(7.5625f*(postFix)*t + 0.984375f) + b); + } +} + +EASEDEF float EaseBounceIn(float t, float b, float c, float d) { return (c - EaseBounceOut(d-t, 0, c, d) + b); } +EASEDEF float EaseBounceInOut(float t, float b, float c, float d) +{ + if (t < d/2) return (EaseBounceIn(t*2, 0, c, d)*0.5f + b); + else return (EaseBounceOut(t*2-d, 0, c, d)*0.5f + c*0.5f + b); +} + +// Elastic Easing functions +EASEDEF float EaseElasticIn(float t, float b, float c, float d) +{ + if (t == 0) return b; + if ((t/=d) == 1) return (b + c); + + float p = d*0.3f; + float a = c; + float s = p/4; + float postFix = a*pow(2, 10*(t-=1)); + + return (-(postFix*sin((t*d-s)*(2*PI)/p )) + b); +} + +EASEDEF float EaseElasticOut(float t, float b, float c, float d) +{ + if (t == 0) return b; + if ((t/=d) == 1) return (b + c); + + float p = d*0.3f; + float a = c; + float s = p/4; + + return (a*pow(2,-10*t)*sin((t*d-s)*(2*PI)/p) + c + b); +} + +EASEDEF float EaseElasticInOut(float t, float b, float c, float d) +{ + if (t == 0) return b; + if ((t/=d/2) == 2) return (b + c); + + float p = d*(0.3f*1.5f); + float a = c; + float s = p/4; + + if (t < 1) + { + float postFix = a*pow(2, 10*(t-=1)); + return -0.5f*(postFix*sin((t*d-s)*(2*PI)/p)) + b; + } + + float postFix = a*pow(2, -10*(t-=1)); + + return (postFix*sin((t*d-s)*(2*PI)/p)*0.5f + c + b); +} + +#ifdef __cplusplus +} +#endif + +#endif // EASINGS_H diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h new file mode 100644 index 000000000..3277d7eaa --- /dev/null +++ b/examples/shapes/raygui.h @@ -0,0 +1,3306 @@ +/******************************************************************************************* +* +* raygui v2.0-dev - A simple and easy-to-use immedite-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: +* +* - WindowBox +* - GroupBox +* - Line +* - Panel +* - Label +* - Button +* - LabelButton --> Label +* - ImageButton --> Button +* - ImageButtonEx --> Button +* - Toggle +* - ToggleGroup --> Toggle +* - CheckBox +* - ComboBox +* - DropdownBox +* - TextBox +* - TextBoxMulti +* - ValueBox --> TextBox +* - Spinner --> Button, ValueBox +* - Slider +* - SliderBar --> Slider +* - ProgressBar +* - StatusBar +* - ScrollPanel +* - ListView --> ListElement +* - ColorPicker --> ColorPanel, ColorBarHue +* - MessageBox +* - DummyRec +* - ScrollBar +* - Grid +* +* It also provides a set of functions for styling the controls based on its properties (size, color). +* +* CONFIGURATION: +* +* #define RAYGUI_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RAYGUI_STATIC (defined by default) +* The generated implementation will stay private inside implementation file and all +* internal symbols and functions will only be visible inside that file. +* +* #define RAYGUI_STANDALONE +* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined +* internally in the library and input management and drawing functions must be provided by +* the user (check library implementation for further details). +* +* #define RAYGUI_RICONS_SUPPORT +* Includes ricons.h header defining a set of 128 icons (binary format) to be used on +* multiple controls and following raygui styles +* +* 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! +* 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 +* 1.1 (01-Jun-2017) Complete review of the library +* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria. +* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria. +* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria. +* +* CONTRIBUTORS: +* Ramon Santamaria: Supervision, review, redesign, update and maintenance... +* 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) +* Albert Martos: Review and testing of the library (2015) +* Ian Eito: Review and testing of the library (2015) +* Kevin Gato: Initial implementation of basic components (2014) +* Daniel Nicolas: Initial implementation of basic components (2014) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2018 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. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYGUI_H +#define RAYGUI_H + +#define RAYGUI_VERSION "2.0-dev" + +#if !defined(RAYGUI_STANDALONE) + #include "raylib.h" +#endif + +#if defined(RAYGUI_IMPLEMENTATION) + #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) + #define RAYGUIDEF __declspec(dllexport) extern // We are building raygui as a Win32 shared library (.dll). + #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) + #define RAYGUIDEF __declspec(dllimport) // We are using raygui as a Win32 shared library (.dll) + #else + #ifdef __cplusplus + #define RAYGUIDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) + #else + #define RAYGUIDEF extern // Functions visible from other files + #endif + #endif +#elif defined(RAYGUI_STATIC) + #define RAYGUIDEF static // Functions just visible to module including this file +#endif + +#include // Required for: atoi() + +//---------------------------------------------------------------------------------- +// 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_PROPS_DEFAULT 16 // Number of standard properties +#define NUM_PROPS_EXTENDED 8 // Number of extended properties + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Some types are required for RAYGUI_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + #ifndef __cplusplus + // Boolean type + #ifndef true + typedef enum { false, true } bool; + #endif + #endif + + // Vector2 type + typedef struct Vector2 { + float x; + float y; + } Vector2; + + // Vector3 type + typedef struct Vector3 { + float x; + float y; + float z; + } Vector3; + + // Color type, RGBA (32bit) + typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + } Color; + + // Rectangle type + typedef struct Rectangle { + int x; + int y; + int width; + int height; + } Rectangle; + + // Texture2D type + typedef struct Texture2D { } Texture2D; + + // Font type + typedef struct Font { } Font; +#endif + +// Gui global state enum +typedef enum { + GUI_STATE_NORMAL = 0, + GUI_STATE_FOCUSED, + GUI_STATE_PRESSED, + GUI_STATE_DISABLED, +} GuiControlState; + +// Gui global text alignment +typedef enum { + GUI_TEXT_ALIGN_LEFT = 0, + GUI_TEXT_ALIGN_CENTER, + GUI_TEXT_ALIGN_RIGHT, +} GuiTextAlignment; + +// Gui standard controls +typedef enum { + DEFAULT = 0, + LABEL, // LABELBUTTON + BUTTON, // IMAGEBUTTON + TOGGLE, // TOGGLEGROUP + SLIDER, // SLIDERBAR + PROGRESSBAR, + CHECKBOX, + COMBOBOX, + DROPDOWNBOX, + TEXTBOX, // VALUEBOX, SPINNER, TEXTBOXMULTI -> TODO: Probably they should not be dependant on TEXTBOX style! + LISTVIEW, + COLORPICKER, + SCROLLBAR +} GuiControlStandard; + +// Gui default properties for every control +typedef enum { + BORDER_COLOR_NORMAL = 0, + BASE_COLOR_NORMAL, + TEXT_COLOR_NORMAL, + BORDER_COLOR_FOCUSED, + BASE_COLOR_FOCUSED, + TEXT_COLOR_FOCUSED, + BORDER_COLOR_PRESSED, + BASE_COLOR_PRESSED, + TEXT_COLOR_PRESSED, + BORDER_COLOR_DISABLED, + BASE_COLOR_DISABLED, + TEXT_COLOR_DISABLED, + BORDER_WIDTH, + INNER_PADDING, + TEXT_ALIGNMENT, + RESERVED02 +} GuiControlProperty; + +// Gui extended properties depending on control type +// NOTE: We reserve a fixed size of additional properties per control (8) + +// Default properties +typedef enum { + TEXT_SIZE = 16, + TEXT_SPACING, + LINE_COLOR, + //LINE_THICK, + BACKGROUND_COLOR, +} GuiDefaultProperty; + +// Label +//typedef enum { } GuiLabelProperty; + +// Button +//typedef enum { } GuiButtonProperty; + +// Toggle / ToggleGroup +typedef enum { + GROUP_PADDING = 16, +} GuiToggleProperty; + +// Slider / SliderBar +typedef enum { + SLIDER_WIDTH = 16, + TEXT_PADDING +} GuiSliderProperty; + +// 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 +} GuiCheckBoxProperty; + +// ComboBox +typedef enum { + SELECTOR_WIDTH = 16, + SELECTOR_PADDING +} GuiComboBoxProperty; + +// DropdownBox +typedef enum { + ARROW_RIGHT_PADDING = 16, +} GuiDropdownBoxProperty; + +// ColorPicker +typedef enum { + COLOR_SELECTOR_SIZE = 16, + BAR_WIDTH, // Lateral bar width + BAR_PADDING, // Lateral bar separation from panel + BAR_SELECTOR_HEIGHT, // Lateral bar selector height + BAR_SELECTOR_PADDING // Lateral bar selector outer padding +} GuiColorPickerProperty; + +// ListView +typedef enum { + ELEMENTS_HEIGHT = 16, + ELEMENTS_PADDING, + SCROLLBAR_WIDTH, + SCROLLBAR_SIDE, // This property defines vertical scrollbar side (SCROLLBAR_LEFT_SIDE or SCROLLBAR_RIGHT_SIDE) +} GuiListViewProperty; + +// ScrollBar +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; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + +// Global gui modification functions +RAYGUIDEF void GuiEnable(void); // Enable gui controls (global state) +RAYGUIDEF void GuiDisable(void); // Disable gui controls (global state) +RAYGUIDEF void GuiLock(void); // Lock gui controls (global state) +RAYGUIDEF void GuiUnlock(void); // Unlock gui controls (global state) +RAYGUIDEF void GuiState(int state); // Set gui state (global state) +RAYGUIDEF void GuiFont(Font font); // Set gui custom font (global state) +RAYGUIDEF void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f + +// Style set/get functions +RAYGUIDEF void GuiSetStyle(int control, int property, int value); // Set one style property +RAYGUIDEF int GuiGetStyle(int control, int property); // Get one style property + +// Container/separator controls, useful for controls organization +RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text); // Window Box control, shows a window that can be closed +RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with title name +RAYGUIDEF void GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text +RAYGUIDEF void GuiPanel(Rectangle bounds); // Panel control, useful to group controls +RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll); // Scroll Panel control + +// Basic controls set +RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text); // Label control, shows text +RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked +RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked +RAYGUIDEF bool GuiImageButton(Rectangle bounds, Texture2D texture); // Image button control, returns true when clicked +RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle texSource, const char *text); // Image button extended control, returns true when clicked +RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active); // Toggle Button control, returns true when active +RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active); // Toggle Group control, returns active toggle index +RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked); // Check Box control, returns true when active +RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active); // Combo Box control, returns selected item index +RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item +RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value +RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers +RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text +RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines +RAYGUIDEF float GuiSlider(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Slider control, returns selected value +RAYGUIDEF float GuiSliderBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Slider Bar control, returns selected value +RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Progress Bar control, shows current progress value +RAYGUIDEF 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 + +// 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 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) +RAYGUIDEF void GuiLoadStyleProps(const int *props, int count); // Load style properties from array +RAYGUIDEF void GuiLoadStyleDefault(void); // Load style default over global style +RAYGUIDEF void GuiUpdateStyleComplete(void); // Updates full style properties set with default values + +/* +typedef GuiStyle (unsigned int *) +RAYGUIDEF GuiStyle LoadGuiStyle(const char *fileName); // Load style from file (.rgs) +RAYGUIDEF void UnloadGuiStyle(GuiStyle style); // Unload style +*/ + +RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended + +#endif // RAYGUI_H + + +/*********************************************************************************** +* +* RAYGUI IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RAYGUI_IMPLEMENTATION) + +#if defined(RAYGUI_RICONS_SUPPORT) + #if defined(RAYGUI_STANDALONE) + #define RICONS_STANDALONE + #endif + + #define RICONS_IMPLEMENTATION + #include "ricons.h" // Required for: raygui icons +#endif + +#include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() +#include // Required for: strlen() on GuiTextBox() + +#if defined(RAYGUI_STANDALONE) + #include // Required for: va_list, va_start(), vfprintf(), va_end() +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Gui control property style element +typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static GuiControlState guiState = GUI_STATE_NORMAL; + +static Font guiFont = { 0 }; // NOTE: Highly coupled to raylib +static bool guiLocked = false; +static float guiAlpha = 1.0f; + +// Global gui style array (allocated on heap by default) +// NOTE: In raygui we manage a single int array with all the possible style properties. +// When a new style is loaded, it loads over the global style... but default gui style +// could always be recovered with GuiLoadStyleDefault() +static unsigned int guiStyle[NUM_CONTROLS*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED)] = { 0 }; +static bool guiStyleLoaded = false; + +//---------------------------------------------------------------------------------- +// Standalone Mode Functions Declaration +// +// NOTE: raygui depend on some raylib input and drawing functions +// To use raygui as standalone library, below functions must be defined by the user +//---------------------------------------------------------------------------------- +#if defined(RAYGUI_STANDALONE) + +#define KEY_RIGHT 262 +#define KEY_LEFT 263 +#define KEY_DOWN 264 +#define KEY_UP 265 +#define KEY_BACKSPACE 259 +#define KEY_ENTER 257 +#define MOUSE_LEFT_BUTTON 0 + +#ifdef __cplusplus + #define CLITERAL +#else + #define CLITERAL (Color) +#endif + +#define WHITE CLITERAL{ 255, 255, 255, 255 } // White +#define BLACK CLITERAL{ 0, 0, 0, 255 } // Black +#define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo) +#define GRAY CLITERAL{ 130, 130, 130, 255 } // Gray -- GuiColorBarAlpha() + +// raylib functions are already implemented in raygui +//------------------------------------------------------------------------------- +static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value +static int ColorToInt(Color color); // Returns hexadecimal value for a Color +static Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle +static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' +//------------------------------------------------------------------------------- + +// Input required functions +//------------------------------------------------------------------------------- +static Vector2 GetMousePosition(void) { return (Vector2){ 0, 0 }; } +static int GetMouseWheelMove(void) { return 0; } +static bool IsMouseButtonDown(int button) { return false; } +static bool IsMouseButtonPressed(int button) { return false; } +static bool IsMouseButtonReleased(int button) { return false; } + +static bool IsKeyDown(int key) { return false; } +static bool IsKeyPressed(int key) { return false; } +static int GetKeyPressed(void) { return 0; } // -- GuiTextBox() +//------------------------------------------------------------------------------- + +// 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 +//------------------------------------------------------------------------------- + +#endif // RAYGUI_STANDALONE + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- + +// List Element control, returns element state +static bool GuiListElement(Rectangle bounds, const char *text, bool active, bool editMode); + +static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB +static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV + +// Gui get text width using default font +static int GetTextWidth(const char *text) // TODO: GetTextSize() +{ + Vector2 size = { 0 }; + + if (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??? + + return (int)size.x; +} + +// Get text bounds considering control bounds +static Rectangle GetTextBounds(int control, Rectangle bounds) +{ + Rectangle textBounds = { 0 }; + + textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING); + textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING); + textBounds.width = bounds.width - 2*(GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING)); + textBounds.height = bounds.height - 2*(GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING)); + + switch (control) + { + case COMBOBOX: bounds.width -= (GuiGetStyle(control, SELECTOR_WIDTH) + GuiGetStyle(control, SELECTOR_PADDING)); break; + case CHECKBOX: bounds.x += (bounds.width + GuiGetStyle(control, CHECK_TEXT_PADDING)); break; + default: break; + } + // TODO: Special cases: COMBOBOX, DROPDOWNBOX, SPINNER, LISTVIEW (scrollbar?) + // More special cases: CHECKBOX, SLIDER + + return textBounds; +} + +// Get text icon if provided and move text cursor +static const char *GetTextIcon(const char *text, int *iconId) +{ +#if defined(RAYGUI_RICONS_SUPPORT) + if (text[0] == '#') // Maybe we have an icon! + { + char iconValue[4] = { 0 }; + + int i = 1; + for (i = 1; i < 4; i++) + { + if ((text[i] != '#') && (text[i] != '\0')) iconValue[i - 1] = text[i]; + else break; + } + + iconValue[3] = '\0'; + *iconId = atoi(iconValue); + + // Move text pointer after icon + // WARNING: If only icon provided, it could point to EOL character! + if (*iconId > 0) text += (i + 1); + } +#endif + + return text; +} + +// Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) +{ + if (guiFont.texture.id == 0) guiFont = GetFontDefault(); + + if ((text != NULL) && (text[0] != '\0')) + { + int iconId = 0; + text = GetTextIcon(text, &iconId); // Check text for icon and move cursor + + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + #define ICON_TEXT_PADDING 4 + + Vector2 position = { bounds.x, bounds.y }; + + // NOTE: We get text size after icon been processed + int textWidth = GetTextWidth(text); + int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); + +#if defined(RAYGUI_RICONS_SUPPORT) + if (iconId > 0) + { + textWidth += RICONS_SIZE; + + // WARNING: If only icon provided, text could be pointing to eof character! + if ((text != NULL) && (text[0] != '\0')) textWidth += ICON_TEXT_PADDING; + } +#endif + + // Check guiTextAlign global variables + switch (alignment) + { + case GUI_TEXT_ALIGN_LEFT: + { + position.x = bounds.x; + position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height); + } break; + case GUI_TEXT_ALIGN_CENTER: + { + position.x = bounds.x + bounds.width/2 - textWidth/2; + position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height); + } break; + case GUI_TEXT_ALIGN_RIGHT: + { + position.x = bounds.x + bounds.width - textWidth; + position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height); + } break; + default: break; + } + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if defined(RAYGUI_RICONS_SUPPORT) + #define ICON_TEXT_PADDING 4 + + if (iconId > 0) + { + // NOTE: We consider icon height, probably different than text size + DrawIcon(iconId, (Vector2){ position.x, bounds.y + bounds.height/2 - RICONS_SIZE/2 + VALIGN_OFFSET(bounds.height) }, 1, tint); + position.x += (RICONS_SIZE + ICON_TEXT_PADDING); + } +#endif + DrawTextEx(guiFont, text, position, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + //--------------------------------------------------------------------------------- + } +} + +// Split controls text into multiple strings +// Also check for multiple columns (required by GuiToggleGroup()) +static const char **GuiTextSplit(const char *text, int *count, int *textRow); + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Enable gui global state +RAYGUIDEF void GuiEnable(void) { guiState = GUI_STATE_NORMAL; } + +// Disable gui global state +RAYGUIDEF void GuiDisable(void) { guiState = GUI_STATE_DISABLED; } + +// Lock gui global state +RAYGUIDEF void GuiLock(void) { guiLocked = true; } + +// Unlock gui global state +RAYGUIDEF void GuiUnlock(void) { guiLocked = false; } + +// Set gui state (global state) +RAYGUIDEF void GuiState(int state) { guiState = (GuiControlState)state; } + +// Define custom gui font +RAYGUIDEF void GuiFont(Font font) +{ + if (font.texture.id > 0) + { + guiFont = font; + GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); + + // 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); + } +} + +// Set gui controls alpha global state +RAYGUIDEF void GuiFade(float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + guiAlpha = alpha; +} + +// Set control style property value +RAYGUIDEF void GuiSetStyle(int control, int property, int value) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + guiStyle[control*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property] = value; +} + +// Get control style property value +RAYGUIDEF int GuiGetStyle(int control, int property) +{ + if (!guiStyleLoaded) GuiLoadStyleDefault(); + return guiStyle[control*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property]; +} + +// Window Box control +RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text) +{ + #define WINDOW_CLOSE_BUTTON_PADDING 2 + #define WINDOW_STATUSBAR_HEIGHT 24 + + GuiControlState state = guiState; + bool clicked = false; + + Rectangle statusBar = { bounds.x, bounds.y, bounds.width, WINDOW_STATUSBAR_HEIGHT }; + if (bounds.height < WINDOW_STATUSBAR_HEIGHT*2) bounds.height = WINDOW_STATUSBAR_HEIGHT*2; + + Rectangle buttonRec = { statusBar.x + statusBar.width - GuiGetStyle(DEFAULT, BORDER_WIDTH) - WINDOW_CLOSE_BUTTON_PADDING - 20, + statusBar.y + GuiGetStyle(DEFAULT, BORDER_WIDTH) + WINDOW_CLOSE_BUTTON_PADDING, 18, 18 }; + // Update control + //-------------------------------------------------------------------- + // NOTE: Logic is directly managed by button + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + + // 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), + bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 }, + Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); + + // Draw window header as status bar + int defaultPadding = GuiGetStyle(DEFAULT, INNER_PADDING); + int defaultTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + GuiSetStyle(DEFAULT, INNER_PADDING, 8); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiStatusBar(statusBar, text); + GuiSetStyle(DEFAULT, INNER_PADDING, defaultPadding); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, defaultTextAlign); + + // Draw window close button + int buttonBorder = GuiGetStyle(BUTTON, BORDER_WIDTH); + int buttonTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); +#if defined(RAYGUI_RICONS_SUPPORT) + clicked = GuiButton(buttonRec, GuiIconText(RICON_CROSS_SMALL, NULL)); +#else + clicked = GuiButton(buttonRec, "x"); +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, buttonBorder); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, buttonTextAlignment); + //-------------------------------------------------------------------- + + return clicked; +} + +// Group Box control with title name +RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text) +{ + #define GROUPBOX_LINE_THICK 1 + #define GROUPBOX_TEXT_PADDING 10 + #define GROUPBOX_PADDING 2 + + GuiControlState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + DrawRectangle(bounds.x, bounds.y, GROUPBOX_LINE_THICK, bounds.height, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + DrawRectangle(bounds.x, bounds.y + bounds.height - 1, bounds.width, GROUPBOX_LINE_THICK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + DrawRectangle(bounds.x + bounds.width - 1, bounds.y, GROUPBOX_LINE_THICK, bounds.height, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + + GuiLine((Rectangle){ bounds.x, bounds.y, bounds.width, 1 }, text); + //-------------------------------------------------------------------- +} + +// Line control +RAYGUIDEF void GuiLine(Rectangle bounds, const char *text) +{ + #define LINE_THICK 1 + #define LINE_TEXT_PADDING 10 + #define LINE_TEXT_SPACING 2 + + GuiControlState state = guiState; + + Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha); + + // Draw control + //-------------------------------------------------------------------- + if (text == NULL) DrawRectangle(bounds.x, bounds.y + bounds.height/2, bounds.width, 1, color); + else + { + Rectangle textBounds = { 0 }; + textBounds.width = GetTextWidth(text) + 2*LINE_TEXT_SPACING; // TODO: Consider text icon + textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x + LINE_TEXT_PADDING + LINE_TEXT_SPACING; + textBounds.y = bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + // Draw line with embedded text label: "--- text --------------" + DrawRectangle(bounds.x, bounds.y, LINE_TEXT_PADDING, 1, color); + GuiLabel(textBounds, text); + DrawRectangle(bounds.x + textBounds.width + LINE_TEXT_PADDING + 2*LINE_TEXT_SPACING, bounds.y, bounds.width - (textBounds.width + LINE_TEXT_PADDING + 2*LINE_TEXT_SPACING), 1, color); + } + //-------------------------------------------------------------------- +} + +// Panel control +RAYGUIDEF void GuiPanel(Rectangle bounds) +{ + #define PANEL_BORDER_WIDTH 1 + + GuiControlState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); + DrawRectangleLinesEx(bounds, PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha)); + //-------------------------------------------------------------------- +} + +// Scroll Panel control +RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll) +{ + GuiControlState state = guiState; + + Vector2 scrollPos = { 0.0f, 0.0f }; + if (scroll != NULL) scrollPos = *scroll; + + bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false; + + // Recheck to account for the other scrollbar being visible + if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false; + + 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) }; + + // 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 }; + + // Clip view area to the actual content size + if (view.width > content.width) view.width = content.width; + if (view.height > content.height) view.height = content.height; + + // TODO: Review! + const int horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? -verticalScrollBarWidth : 0) - GuiGetStyle(DEFAULT, BORDER_WIDTH) : ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? -verticalScrollBarWidth : 0) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + const int horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? verticalScrollBarWidth : 0) : -GuiGetStyle(DEFAULT, BORDER_WIDTH); + const int verticalMin = hasVerticalScrollBar? -GuiGetStyle(DEFAULT, BORDER_WIDTH) : -GuiGetStyle(DEFAULT, BORDER_WIDTH); + const int verticalMax = hasVerticalScrollBar? content.height - bounds.height + horizontalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) : -GuiGetStyle(DEFAULT, BORDER_WIDTH); + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + + if (hasHorizontalScrollBar) + { + if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } + + if (hasVerticalScrollBar) + { + if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + } + + scrollPos.y += GetMouseWheelMove()*20; + } + } + + // Normalize scroll values + if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin; + if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax; + if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin; + if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Save size of the scrollbar slider + const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); + + // Draw horizontal scrollbar if visible + if (hasHorizontalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content width and the widget width + GuiSetStyle(SCROLLBAR, SLIDER_SIZE, ((bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/content.width)*(bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)); + scrollPos.x = -GuiScrollBar(horizontalScrollBar, -scrollPos.x, horizontalMin, horizontalMax); + } + + // Draw vertical scrollbar if visible + if (hasVerticalScrollBar) + { + // Change scrollbar slider size to show the diff in size between the content height and the widget height + GuiSetStyle(SCROLLBAR, SLIDER_SIZE, ((bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/content.height)* (bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)); + scrollPos.y = -GuiScrollBar(verticalScrollBar, -scrollPos.y, verticalMin, verticalMax); + } + + // Draw detail corner rectangle if both scroll bars are visible + 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)); + } + + // 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)); + //-------------------------------------------------------------------- + + if (scroll != NULL) *scroll = scrollPos; + + return view; +} + +// Label control +RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text) +{ + GuiControlState state = guiState; + + // Update control + //-------------------------------------------------------------------- + // ... + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, (state == GUI_STATE_DISABLED)? TEXT_COLOR_DISABLED : TEXT_COLOR_NORMAL)), guiAlpha)); + //-------------------------------------------------------------------- +} + +// Button control, returns true when clicked +RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text) +{ + GuiControlState state = guiState; + bool pressed = false; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.y + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); + + GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); + //------------------------------------------------------------------ + + return pressed; +} + +// Label button control +RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text) +{ + GuiControlState state = guiState; + bool pressed = false; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check checkbox state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// 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); +} + +// Image button control, returns true when clicked +RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle texSource, const char *text) +{ + GuiControlState state = guiState; + bool clicked = false; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) clicked = true; + else state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.y + GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); + + 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)); + //------------------------------------------------------------------ + + return clicked; +} + +// Toggle Button control, returns true when active +RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active) +{ + GuiControlState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check toggle button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + state = GUI_STATE_NORMAL; + active = !active; + } + else state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state == GUI_STATE_NORMAL) + { + DrawRectangleLinesEx(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.y + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); + + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, (active? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); + } + else + { + DrawRectangleLinesEx(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.y + GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha)); + + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha)); + } + //-------------------------------------------------------------------- + + return active; +} + +// Toggle Group control, returns toggled button index +RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active) +{ + float initBoundsX = bounds.x; + + // Get substrings elements from text (elements pointers) + int rows[64] = { 0 }; + int elementsCount = 0; + const char **elementsPtrs = GuiTextSplit(text, &elementsCount, rows); + + int prevRow = rows[0]; + + for (int i = 0; i < elementsCount; i++) + { + if (prevRow != rows[i]) + { + bounds.x = initBoundsX; + bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING)); + prevRow = rows[i]; + } + + if (i == active) GuiToggle(bounds, elementsPtrs[i], true); + else if (GuiToggle(bounds, elementsPtrs[i], false) == true) active = i; + + bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING)); + } + + return active; +} + +// Check Box control, returns true when active +RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) +{ + GuiControlState state = guiState; + + Rectangle textBounds = { 0 }; + textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + textBounds.width = GetTextWidth(text); // TODO: Consider text icon + textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE); + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + 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 (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) checked = !checked; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha)); + if (checked) DrawRectangle(bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING), + bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING), + bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING)), + bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING)), + Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)), guiAlpha)); + + // NOTE: Forced left text alignment + GuiDrawText(text, textBounds, GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + return checked; +} + +// Combo Box control, returns selected item index +RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active) +{ + GuiControlState state = guiState; + + 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 }; + + // Get substrings elements from text (elements pointers, lengths and count) + int elementsCount = 0; + const char **elementsPtrs = GuiTextSplit(text, &elementsCount, NULL); + + if (active < 0) active = 0; + else if (active > elementsCount - 1) active = elementsCount - 1; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + active += 1; + if (active >= elementsCount) active = 0; + } + + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw combo box main + DrawRectangleLinesEx(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(COMBOBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(COMBOBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(COMBOBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha)); + + GuiDrawText(elementsPtrs[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); + + // Draw selector using a custom button + // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + 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); + //-------------------------------------------------------------------- + + return active; +} + +// Dropdown Box control, returns selected item +RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) +{ + GuiControlState state = guiState; + + // Get substrings elements from text (elements pointers, lengths and count) + int elementsCount = 0; + const char **elementsPtrs = GuiTextSplit(text, &elementsCount, NULL); + + bool pressed = false; + int auxActive = *active; + + Rectangle closeBounds = bounds; + Rectangle openBounds = bounds; + + openBounds.height *= (elementsCount + 1); + + // Update control + //-------------------------------------------------------------------- + if (guiLocked && editMode) guiLocked = false; + + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) state = GUI_STATE_PRESSED; + + if (!editMode) + { + if (CheckCollisionPointRec(mousePoint, closeBounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + else state = GUI_STATE_FOCUSED; + } + } + else + { + if (CheckCollisionPointRec(mousePoint, closeBounds)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + } + else if (!CheckCollisionPointRec(mousePoint, openBounds)) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + } + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + + // TODO: Review this ugly hack... DROPDOWNBOX depends on GiListElement() that uses DEFAULT_TEXT_ALIGNMENT + int tempTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT)); + + switch (state) + { + case GUI_STATE_NORMAL: + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_NORMAL)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_NORMAL)), guiAlpha)); + + GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); + } break; + case GUI_STATE_FOCUSED: + { + GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, editMode); + } break; + case GUI_STATE_PRESSED: + { + if (!editMode) GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); + if (editMode) + { + GuiPanel(openBounds); + + GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true); + + for (int i = 0; i < elementsCount; i++) + { + if (i == auxActive && editMode) + { + if (GuiListElement((Rectangle){ bounds.x, bounds.y + bounds.height*(i + 1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), + bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, + elementsPtrs[i], true, true) == false) pressed = true; + } + else + { + if (GuiListElement((Rectangle){ bounds.x, bounds.y + bounds.height*(i+1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING), + bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) }, + elementsPtrs[i], false, true)) + { + auxActive = i; + pressed = true; + } + } + } + } + } break; + case GUI_STATE_DISABLED: + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_DISABLED)), guiAlpha)); + + GuiListElement((Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false); + } break; + default: break; + } + + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, tempTextAlign); + + DrawTriangle((Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2 }, + (Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 5, bounds.y + bounds.height/2 - 2 + 5 }, + (Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 10, bounds.y + bounds.height/2 - 2 }, + Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + *active = auxActive; + return pressed; +} + +// Spinner control, returns selected value +// NOTE: Requires static variables: framesCounter, valueSpeed - ERROR! +RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) +{ + bool 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 }; + + // Update control + //-------------------------------------------------------------------- + if (!editMode) + { + if (tempValue < minValue) tempValue = minValue; + if (tempValue > maxValue) tempValue = maxValue; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + 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)); + + int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + +#if defined(RAYGUI_RICONS_SUPPORT) + if (GuiButton(leftButtonBound, GuiIconText(RICON_ARROW_LEFT_FILL, NULL))) tempValue--; + if (GuiButton(rightButtonBound, GuiIconText(RICON_ARROW_RIGHT_FILL, NULL))) tempValue++; +#else + if (GuiButton(leftButtonBound, "<")) tempValue--; + if (GuiButton(rightButtonBound, ">")) tempValue++; +#endif + + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + //-------------------------------------------------------------------- + + *value = tempValue; + return pressed; +} + +// Value Box control, updates input text with numbers +// NOTE: Requires static variables: framesCounter +RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode) +{ + #define VALUEBOX_MAX_CHARS 32 + + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + char text[VALUEBOX_MAX_CHARS + 1] = "\0"; + sprintf(text, "%i", *value); + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + bool valueHasChanged = false; + + if (editMode) + { + state = GUI_STATE_PRESSED; + + framesCounter++; + + int keyCount = strlen(text); + + // Only allow keys in range [48..57] + if (keyCount < VALUEBOX_MAX_CHARS) + { + int maxWidth = (bounds.width - (GuiGetStyle(DEFAULT, INNER_PADDING)*2)); + if (GetTextWidth(text) < maxWidth) + { + int key = GetKeyPressed(); + if ((key >= 48) && (key <= 57)) + { + text[keyCount] = (char)key; + keyCount++; + valueHasChanged = true; + } + } + } + + // Delete text + if (keyCount > 0) + { + if (IsKeyPressed(KEY_BACKSPACE)) + { + keyCount--; + text[keyCount] = '\0'; + framesCounter = 0; + if (keyCount < 0) keyCount = 0; + valueHasChanged = true; + } + else if (IsKeyDown(KEY_BACKSPACE)) + { + if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--; + text[keyCount] = '\0'; + if (keyCount < 0) keyCount = 0; + valueHasChanged = true; + } + } + + if (valueHasChanged) *value = atoi(text); + } + else + { + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; + } + + if (!editMode) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(0)) pressed = true; + } + } + else + { + if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) pressed = true; + } + + if (pressed) framesCounter = 0; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); + + if (state == GUI_STATE_PRESSED) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_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)); + } + else if (state == GUI_STATE_DISABLED) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + } + + GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// Text Box control, updates input text +// NOTE 1: Requires static variables: framesCounter +// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) +RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +{ + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) + { + state = GUI_STATE_PRESSED; + framesCounter++; + + int key = GetKeyPressed(); + int keyCount = strlen(text); + + // Only allow keys in range [32..125] + if (keyCount < (textSize - 1)) + { + int maxWidth = (bounds.width - (GuiGetStyle(DEFAULT, INNER_PADDING)*2)); + + if (GetTextWidth(text) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + if (((key >= 32) && (key <= 125)) || + ((key >= 128) && (key < 255))) + { + text[keyCount] = (char)key; + keyCount++; + } + } + } + + // Delete text + if (keyCount > 0) + { + if (IsKeyPressed(KEY_BACKSPACE)) + { + keyCount--; + text[keyCount] = '\0'; + framesCounter = 0; + if (keyCount < 0) keyCount = 0; + } + else if (IsKeyDown(KEY_BACKSPACE)) + { + if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--; + text[keyCount] = '\0'; + if (keyCount < 0) keyCount = 0; + } + } + } + + if (!editMode) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(0)) pressed = true; + } + } + else + { + if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) pressed = true; + } + + if (pressed) framesCounter = 0; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); + + if (state == GUI_STATE_PRESSED) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), 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)); + } + else if (state == GUI_STATE_DISABLED) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + } + + GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// Text Box control with multiple lines +RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) +{ + static int framesCounter = 0; // Required for blinking cursor + + GuiControlState state = guiState; + bool pressed = false; + + bool textHasChange = false; + 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) + { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) + { + state = GUI_STATE_PRESSED; + + framesCounter++; + + int keyCount = strlen(text); + int maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2)); + int maxHeight = (bounds.height - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2)); + + //numChars = TextFormat("%i/%i", keyCount, textSize - 1); + + // Only allow keys in range [32..125] + if (keyCount < (textSize - 1)) + { + int key = GetKeyPressed(); + + if (MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), 1).y < (maxHeight - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + if (IsKeyPressed(KEY_ENTER)) + { + text[keyCount] = '\n'; + keyCount++; + } + else if (((key >= 32) && (key <= 125)) || + ((key >= 128) && (key < 255))) + { + text[keyCount] = (char)key; + keyCount++; + textHasChange = true; + } + } + else if (GetTextWidth(strrchr(text, '\n')) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + if (((key >= 32) && (key <= 125)) || + ((key >= 128) && (key < 255))) + { + text[keyCount] = (char)key; + keyCount++; + textHasChange = true; + } + } + } + + // Delete text + if (keyCount > 0) + { + if (IsKeyPressed(KEY_BACKSPACE)) + { + keyCount--; + text[keyCount] = '\0'; + framesCounter = 0; + if (keyCount < 0) keyCount = 0; + textHasChange = true; + } + else if (IsKeyDown(KEY_BACKSPACE)) + { + if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--; + text[keyCount] = '\0'; + if (keyCount < 0) keyCount = 0; + textHasChange = true; + } + } + + // Introduce automatic new line if necessary + if (textHasChange) + { + textHasChange = false; + + char *lastLine = strrchr(text, '\n'); + int maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2)); + + if (lastLine != NULL) + { + if (GetTextWidth(lastLine) > maxWidth) + { + int firstIndex = lastLine - text; + + char *lastSpace = strrchr(lastLine, 32); + + if (lastSpace != NULL) + { + int secondIndex = lastSpace - lastLine; + text[firstIndex + secondIndex] = '\n'; + } + else + { + int len = (lastLine != NULL)? strlen(lastLine) : 0; + char lastChar = lastLine[len - 1]; + lastLine[len - 1] = '\n'; + lastLine[len] = lastChar; + lastLine[len + 1] = '\0'; + keyCount++; + } + } + } + else + { + if (GetTextWidth(text) > maxWidth) + { + char *lastSpace = strrchr(text, 32); + + if (lastSpace != NULL) + { + int index = lastSpace - text; + text[index] = '\n'; + } + else + { + int len = (lastLine != NULL)? strlen(lastLine) : 0; + char lastChar = lastLine[len - 1]; + lastLine[len - 1] = '\n'; + lastLine[len] = lastChar; + lastLine[len + 1] = '\0'; + keyCount++; + } + } + } + } + + // Counting how many new lines + for (int i = 0; i < keyCount; i++) + { + if (text[i] == '\n') currentLine++; + } + } + + // Changing edit mode + if (!editMode) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(0)) pressed = true; + } + } + else + { + if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0)) pressed = true; + } + + if (pressed) framesCounter = 0; + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha)); + + if (state == GUI_STATE_PRESSED) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_FOCUSED)), guiAlpha)); + + if (editMode) + { + if ((framesCounter/20)%2 == 0) + { + char *line = NULL; + if (currentLine > 0) line = strrchr(text, '\n'); + else line = text; + + // Draw text cursor + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING) + GetTextWidth(line), + bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING)/2 + ((GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(TEXTBOX, INNER_PADDING))*currentLine), + 1, GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(TEXTBOX, INNER_PADDING), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_FOCUSED)), guiAlpha)); + } + + // Draw characters counter + //GuiDrawText(numChars, (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) + { + DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + } + + GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + return pressed; +} + +// Slider control with pro parameters +// NOTE: Other GuiSlider*() controls use this one +RAYGUIDEF float GuiSliderPro(Rectangle bounds, const char *text, float value, float minValue, float maxValue, int sliderWidth, bool showValue) +{ + GuiControlState state = guiState; + + int sliderValue = (int)(((value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + + Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, INNER_PADDING), + 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, INNER_PADDING) }; + + if (sliderWidth > 0) // Slider + { + slider.x += (sliderValue - sliderWidth/2); + slider.width = sliderWidth; + } + else if (sliderWidth == 0) // SliderBar + { + slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH); + slider.width = sliderValue; + } + + Rectangle textBounds = { 0 }; + textBounds.width = GetTextWidth(text); // TODO: Consider text icon + textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = GUI_STATE_PRESSED; + + // Get equivalent value and slider position from mousePoint.x + value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + + if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider + else if (sliderWidth == 0) slider.width = sliderValue; // SliderBar + } + else state = GUI_STATE_FOCUSED; + } + + if (value > maxValue) value = maxValue; + else if (value < minValue) value = minValue; + } + + // Bar limits check + if (sliderWidth > 0) // Slider + { + if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH); + else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH); + } + else if (sliderWidth == 0) // SliderBar + { + if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha)); + DrawRectangle(bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, (state == GUI_STATE_NORMAL)? BASE_COLOR_PRESSED : (BASE + (state*3)))), 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, + Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + //-------------------------------------------------------------------- + + return value; +} + +// Slider control extended, returns selected value and has text +RAYGUIDEF float GuiSlider(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue) +{ + return GuiSliderPro(bounds, text, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH), showValue); +} + +// Slider Bar control extended, returns selected value +RAYGUIDEF float GuiSliderBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue) +{ + return GuiSliderPro(bounds, text, value, minValue, maxValue, 0, showValue); +} + +// Progress Bar control extended, shows current progress value +RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue) +{ + GuiControlState state = guiState; + + Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), + bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, INNER_PADDING), 0, + bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, INNER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if (state != GUI_STATE_DISABLED) progress.width = (int)(value/(maxValue - minValue)*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); + //-------------------------------------------------------------------- + + // 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)); + + 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)); + //-------------------------------------------------------------------- + + return value; +} + +// Status Bar control +RAYGUIDEF void GuiStatusBar(Rectangle bounds, const char *text) +{ + GuiControlState state = guiState; + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha)); + DrawRectangleRec((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)); + //-------------------------------------------------------------------- +} + +// Dummy rectangle control, intended for placeholding +RAYGUIDEF void GuiDummyRec(Rectangle bounds, const char *text) +{ + GuiControlState state = guiState; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check button state + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + + GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != GUI_STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + //------------------------------------------------------------------ +} + +// Scroll Bar control +RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) +{ + GuiControlState state = guiState; + + // Is the scrollbar horizontal or vertical? + 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; + + // Spinner buttons [<] [>] [∧] [∨] + Rectangle spinnerUpLeft, spinnerDownRight; + // Actual area of the scrollbar excluding the spinner buttons + Rectangle scrollbar; // ------------ + // Slider bar that moves --[///]----- + Rectangle slider; + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + + 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 }; + + 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) }; + 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 }; + } + 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))}; + 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)) }; + } + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = GUI_STATE_FOCUSED; + + // Handle mouse wheel + int wheel = GetMouseWheelMove(); + if (wheel != 0) value += wheel; + + 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); + + state = GUI_STATE_PRESSED; + } + else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (!isVertical) + { + Rectangle scrollArea = { spinnerUpLeft.x + spinnerUpLeft.width, spinnerUpLeft.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}; + if (CheckCollisionPointRec(mousePoint, scrollArea)) value = ((float)(mousePoint.y - scrollArea.y - slider.height/2)*range)/(scrollArea.height - slider.height) + minValue; + } + } + } + + // Normalize value + if (value > maxValue) value = maxValue; + if (value < minValue) value = minValue; + } + //-------------------------------------------------------------------- + + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background + DrawRectangleRec(scrollbar, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background + + DrawRectangleLinesEx(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha)); + + DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar + + // Draw arrows using lines + const int padding = (spinnerSize - GuiGetStyle(SCROLLBAR, ARROWS_SIZE))/2; + const Vector2 lineCoords[] = + { + //coordinates for < 0,1,2 + {spinnerUpLeft.x + padding, spinnerUpLeft.y + spinnerSize/2}, + {spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + padding }, + {spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + spinnerSize - padding}, + + //coordinates for > 3,4,5 + {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, + {spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + spinnerSize/2 }, + {spinnerDownRight.x + padding, spinnerDownRight.y + spinnerSize - padding}, + + //coordinates for ∧ 6,7,8 + {spinnerUpLeft.x + spinnerSize/2, spinnerUpLeft.y + padding}, + {spinnerUpLeft.x + padding, spinnerUpLeft.y + spinnerSize - padding}, + {spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + spinnerSize - padding}, + + //coordinates for ∨ 9,10,11 + {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, + {spinnerDownRight.x + spinnerSize/2, spinnerDownRight.y + spinnerSize - padding }, + {spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + padding} + }; + + Color lineColor = Fade(GetColor(GuiGetStyle(BUTTON, TEXT + state*3)), guiAlpha); + + if (GuiGetStyle(SCROLLBAR, SHOW_SPINNER_BUTTONS)) + { + 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); + } + 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); + } + } + //-------------------------------------------------------------------- + + return value; +} + +// List Element control, returns element state +static bool GuiListElement(Rectangle bounds, const char *text, bool active, bool editMode) +{ + GuiControlState state = guiState; + + if (!guiLocked && editMode) state = GUI_STATE_NORMAL; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (!active) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED; + else state = GUI_STATE_FOCUSED; + } + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) active = !active; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw element rectangle + switch (state) + { + case GUI_STATE_NORMAL: + { + if (active) + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha)); + } + } break; + case GUI_STATE_FOCUSED: + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha)); + } break; + case GUI_STATE_PRESSED: + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha)); + } break; + case GUI_STATE_DISABLED: + { + if (active) + { + DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), guiAlpha)); + } + } break; + default: break; + } + + // Draw text depending on state + if (state == GUI_STATE_NORMAL) GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, active? TEXT_COLOR_PRESSED : TEXT_COLOR_NORMAL)), guiAlpha)); + else if (state == GUI_STATE_DISABLED) GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, active? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + else GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + state*3)), guiAlpha)); + //-------------------------------------------------------------------- + + return active; +} + +// List View control +RAYGUIDEF bool GuiListView(Rectangle bounds, const char *text, int *active, int *scrollIndex, bool editMode) +{ + bool result = 0; + + int count = 0; + const char **textList = GuiTextSplit(text, &count, NULL); + + result = GuiListViewEx(bounds, textList, count, NULL, active, NULL, scrollIndex, editMode); + + return result; +} + +// List View control extended parameters +// NOTE: Elements could be disabled individually and focused element could be obtained: +// int *enabled defines an array with enabled elements inside the list +// int *focus returns focused element (may be not pressed) +RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int *enabled, int *active, int *focus, int *scrollIndex, bool editMode) +{ + GuiControlState state = guiState; + bool pressed = false; + + int focusElement = -1; + int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex; + bool useScrollBar = true; + bool pressedKey = false; + + int visibleElements = bounds.height/(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)); + if ((startIndex < 0) || (startIndex > count - visibleElements)) startIndex = 0; + int endIndex = startIndex + visibleElements; + + int auxActive = *active; + + float barHeight = bounds.height; + float minBarHeight = 10; + + // Update control + //-------------------------------------------------------------------- + // All the elements fit inside ListView and dont need scrollbar. + if (visibleElements >= count) + { + useScrollBar = false; + startIndex = 0; + endIndex = count; + } + + // Calculate position X and width to draw each element. + int posX = bounds.x + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING); + int elementWidth = bounds.width - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + + if (useScrollBar) + { + posX = GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE? posX + GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : posX; + elementWidth = bounds.width - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + } + + Rectangle scrollBarRect = { bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), 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) }; + + if ((state != GUI_STATE_DISABLED) && !guiLocked) // && !guiLocked + { + Vector2 mousePoint = GetMousePosition(); + + if (editMode) + { + state = GUI_STATE_PRESSED; + + // Change active with keys + if (IsKeyPressed(KEY_UP)) + { + if (auxActive > 0) + { + auxActive--; + if ((useScrollBar) && (auxActive < startIndex)) startIndex--; + } + + pressedKey = true; + } + else if (IsKeyPressed(KEY_DOWN)) + { + if (auxActive < count - 1) + { + auxActive++; + if ((useScrollBar) && (auxActive >= endIndex)) startIndex++; + } + + pressedKey = true; + } + + if (useScrollBar) + { + endIndex = startIndex + visibleElements; + if (CheckCollisionPointRec(mousePoint, viewArea)) + { + int wheel = GetMouseWheelMove(); + + if (wheel < 0 && endIndex < count) startIndex -= wheel; + else if (wheel > 0 && startIndex > 0) startIndex -= wheel; + } + + if (pressedKey) + { + pressedKey = false; + if ((auxActive < startIndex) || (auxActive >= endIndex)) startIndex = auxActive; + } + + if (startIndex < 0) startIndex = 0; + else if (startIndex > (count - (endIndex - startIndex))) + { + startIndex = count - (endIndex - startIndex); + } + + endIndex = startIndex + visibleElements; + + if (endIndex > count) endIndex = count; + } + } + + if (!editMode) + { + if (CheckCollisionPointRec(mousePoint, viewArea)) + { + state = GUI_STATE_FOCUSED; + if (IsMouseButtonPressed(0)) pressed = true; + + startIndex -= GetMouseWheelMove(); + + if (startIndex < 0) startIndex = 0; + else if (startIndex > (count - (endIndex - startIndex))) + { + startIndex = count - (endIndex - startIndex); + } + + pressed = true; + } + } + else + { + if (!CheckCollisionPointRec(mousePoint, viewArea)) + { + if (IsMouseButtonPressed(0) || (GetMouseWheelMove() != 0)) pressed = true; + } + } + + // Get focused element + for (int i = startIndex; i < endIndex; i++) + { + if (CheckCollisionPointRec(mousePoint, (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) })) + { + focusElement = i; + } + } + } + + const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); // Save default slider size + + // Calculate percentage of visible elements and apply same percentage to scrollbar + if (useScrollBar) + { + float percentVisible = (endIndex - startIndex)*100/count; + barHeight *= percentVisible/100; + + if (barHeight < minBarHeight) barHeight = minBarHeight; + else if (barHeight > bounds.height) barHeight = bounds.height; + + GuiSetStyle(SCROLLBAR, SLIDER_SIZE, barHeight); // Change slider size + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + + // Draw scrollBar + if (useScrollBar) + { + const int scrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleElements); // Hack to make the spinner buttons work + + int index = scrollIndex != NULL? *scrollIndex : startIndex; + index = GuiScrollBar(scrollBarRect, index, 0, count - visibleElements); + + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, scrollSpeed); // Reset scroll speed to default + GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider); // Reset slider size to default + + // FIXME: Quick hack to make this thing work, think of a better way + if (scrollIndex != NULL && CheckCollisionPointRec(GetMousePosition(), scrollBarRect) && IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + startIndex = index; + if (startIndex < 0) startIndex = 0; + if (startIndex > (count - (endIndex - startIndex))) + { + startIndex = count - (endIndex - startIndex); + } + + endIndex = startIndex + visibleElements; + + if (endIndex > count) endIndex = count; + } + } + + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha)); + + // Draw ListView states + switch (state) + { + case GUI_STATE_NORMAL: + { + for (int i = startIndex; i < endIndex; i++) + { + 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); + 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); + 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); + } + } break; + case GUI_STATE_FOCUSED: + { + for (int i = startIndex; i < endIndex; i++) + { + 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); + 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); + } + } break; + case GUI_STATE_PRESSED: + { + for (int i = startIndex; i < endIndex; i++) + { + 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); + 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; + } + 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; + } + } + } break; + case GUI_STATE_DISABLED: + { + 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); + } + } break; + default: break; + } + //-------------------------------------------------------------------- + + if (scrollIndex != NULL) *scrollIndex = startIndex; + if (focus != NULL) *focus = focusElement; + *active = auxActive; + + return pressed; +} + +// Color Panel control +RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color) +{ + GuiControlState state = guiState; + Vector2 pickerSelector = { 0 }; + + Vector3 vcolor = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f }; + Vector3 hsv = ConvertRGBtoHSV(vcolor); + + 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 rgbHue = ConvertHSVtoRGB(maxHue); + Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), + (unsigned char)(255.0f*rgbHue.y), + (unsigned char)(255.0f*rgbHue.z), 255 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = GUI_STATE_PRESSED; + pickerSelector = mousePoint; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + hsv.y = colorPick.x; + hsv.z = 1.0f - colorPick.y; + + Vector3 rgb = ConvertHSVtoRGB(hsv); + + // NOTE: Vector3ToColor() only available on raylib 1.8.1 + color = (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) }; + + } + else state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + 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)); + + // 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)); + } + 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)); + } + + DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + //-------------------------------------------------------------------- + + return color; +} + +// Color Bar Alpha control +// NOTE: Returns alpha value normalized [0..1] +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 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = GUI_STATE_PRESSED; + selector.x = mousePoint.x - selector.width/2; + + alpha = (mousePoint.x - bounds.x)/bounds.width; + if (alpha <= 0.0f) alpha = 0.0f; + if (alpha >= 1.0f) alpha = 1.0f; + //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2; + } + else state = GUI_STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + // Draw alpha bar: checked background + if (state != GUI_STATE_DISABLED) + { + for (int i = 0; i < bounds.width/COLORBARALPHA_CHECKED_SIZE; i++) DrawRectangle(bounds.x + COLORBARALPHA_CHECKED_SIZE*(i%((int)bounds.width/COLORBARALPHA_CHECKED_SIZE)), bounds.y, bounds.width/(bounds.width/COLORBARALPHA_CHECKED_SIZE), COLORBARALPHA_CHECKED_SIZE, (i%2)? Fade(Fade(GRAY, 0.4f), guiAlpha) : Fade(Fade(RAYWHITE, 0.4f), guiAlpha)); + for (int i = 0; i < bounds.width/COLORBARALPHA_CHECKED_SIZE; i++) DrawRectangle(bounds.x + COLORBARALPHA_CHECKED_SIZE*(i%((int)bounds.width/COLORBARALPHA_CHECKED_SIZE)), bounds.y + COLORBARALPHA_CHECKED_SIZE, bounds.width/(bounds.width/COLORBARALPHA_CHECKED_SIZE), COLORBARALPHA_CHECKED_SIZE, (i%2)? Fade(Fade(RAYWHITE, 0.4f), guiAlpha) : Fade(Fade(GRAY, 0.4f), guiAlpha)); + DrawRectangleGradientH(bounds.x, bounds.y, bounds.width, bounds.height, Fade((Color){ 255,255,255,0 }, guiAlpha), Fade((Color){ 0,0,0,255 }, guiAlpha)); + } + else DrawRectangleGradientH(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); + + DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + + 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; + } + //-------------------------------------------------------------------- + + return alpha; +} + +// Color Bar Hue control +// NOTE: Returns hue value normalized [0..1] +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) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds) || + CheckCollisionPointRec(mousePoint, selector)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = GUI_STATE_PRESSED; + selector.y = mousePoint.y - selector.height/2; + + hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (hue <= 0.0f) hue = 0.0f; + if (hue >= 359.0f) hue = 359.0f; + + } + else state = GUI_STATE_FOCUSED; + + /*if (IsKeyDown(KEY_UP)) + { + hue -= 2.0f; + if (hue <= 0.0f) hue = 0.0f; + } + else if (IsKeyDown(KEY_DOWN)) + { + hue += 2.0f; + if (hue >= 360.0f) hue = 360.0f; + }*/ + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + 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)); + } + + // 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)); + //-------------------------------------------------------------------- + + return hue; +} + +// TODO: Color GuiColorBarSat() [WHITE->color] +// TODO: Color GuiColorBarValue() [BLACK->color], HSV / HSL +// TODO: float GuiColorBarLuminance() [BLACK->WHITE] + +// Color Picker control +// NOTE: It's divided in multiple controls: +// Color GuiColorPanel() - Color select panel +// float GuiColorBarAlpha(Rectangle bounds, float alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanel() size +RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color) +{ + color = GuiColorPanel(bounds, color); + + Rectangle boundsHue = { bounds.x + bounds.width + GuiGetStyle(COLORPICKER, BAR_PADDING), bounds.y, GuiGetStyle(COLORPICKER, BAR_WIDTH), 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 }); + 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 }; + + return color; +} + +// Message Box control +RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const char *message, const char *buttons) +{ + #define MESSAGEBOX_BUTTON_HEIGHT 24 + #define MESSAGEBOX_BUTTON_PADDING 10 + + int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button + + int buttonsCount = 0; + const char **buttonsText = GuiTextSplit(buttons, &buttonsCount, NULL); + + Vector2 textSize = MeasureTextEx(guiFont, message, GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + + Rectangle textBounds = { 0 }; + textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; + textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + (bounds.height - WINDOW_STATUSBAR_HEIGHT)/4 - textSize.y/2; + textBounds.width = textSize.x; + textBounds.height = textSize.y; + + Rectangle buttonBounds = { 0 }; + buttonBounds.x = bounds.x + MESSAGEBOX_BUTTON_PADDING; + buttonBounds.y = bounds.y + bounds.height/2 + bounds.height/4 - MESSAGEBOX_BUTTON_HEIGHT/2; + buttonBounds.width = (bounds.width - MESSAGEBOX_BUTTON_PADDING*(buttonsCount + 1))/buttonsCount; + buttonBounds.height = MESSAGEBOX_BUTTON_HEIGHT; + + // Draw control + //-------------------------------------------------------------------- + if (GuiWindowBox(bounds, windowTitle)) clicked = 0; + + int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + GuiLabel(textBounds, message); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); + + 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; +} + +// Grid control +// NOTE: Returns grid mouse-hover selected cell +// About drawing lines at subpixel spacing, simple put, not easy solution: +// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster +RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) +{ + #define GRID_COLOR_ALPHA 0.15f // Grid lines alpha amount + + GuiControlState state = guiState; + Vector2 mousePoint = GetMousePosition(); + Vector2 currentCell = { -1, -1 }; + + int linesV = ((int)(bounds.width/spacing) + 1)*subdivs; + int linesH = ((int)(bounds.height/spacing) + 1)*subdivs; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + if (CheckCollisionPointRec(mousePoint, bounds)) + { + currentCell.x = (int)((mousePoint.x - bounds.x)/spacing); + currentCell.y = (int)((mousePoint.y - bounds.y)/spacing); + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + switch (state) + { + case GUI_STATE_NORMAL: + { + // 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)); + } + + // 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)); + } + + } break; + default: break; + } + + return currentCell; +} + +//---------------------------------------------------------------------------------- +// Styles loading functions +//---------------------------------------------------------------------------------- + +// Load raygui style file (.rgs) +RAYGUIDEF void GuiLoadStyle(const char *fileName) +{ + FILE *rgsFile = fopen(fileName, "rb"); + + if (rgsFile != NULL) + { + unsigned int value = 0; + + char signature[5] = ""; + short version = 0; + short numControls = 0; + short numPropsDefault = 0; + short numPropsExtended = 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); + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'S') && + (signature[3] == ' ')) + { + for (int i = 0; i < NUM_CONTROLS; i++) + { + for (int j = 0; j < NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED; j++) + { + fread(&value, 1, sizeof(unsigned int), rgsFile); + GuiSetStyle(i, j, value); + } + } + + // 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); + + if (fontDataSize > 0) + { + Font font = { 0 }; + int fontType = 0; // 0-Normal, 1-SDF + Rectangle whiteRec = { 0 }; + + fread(&font.baseSize, 1, sizeof(int), rgsFile); + fread(&font.charsCount, 1, sizeof(int), rgsFile); + fread(&fontType, 1, sizeof(int), rgsFile); + + // Load font white rectangle + fread(&whiteRec, 1, sizeof(Rectangle), rgsFile); + + // Load font image parameters + int fontImageSize = 0; + fread(&fontImageSize, 1, sizeof(int), rgsFile); + + if (fontImageSize > 0) + { + Image imFont = { 0 }; + imFont.mipmaps = 1; + 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); + + font.texture = LoadTextureFromImage(imFont); + UnloadImage(imFont); + } + + // Load font chars data + 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.chars[i].value, 1, sizeof(int), rgsFile); + fread(&font.chars[i].offsetX, 1, sizeof(int), rgsFile); + fread(&font.chars[i].offsetY, 1, sizeof(int), rgsFile); + fread(&font.chars[i].advanceX, 1, sizeof(int), rgsFile); + } + + GuiFont(font); + + // Set font texture source rectangle to be used as white texture to draw shapes + // NOTE: This way, all gui can be draw using a single draw call + if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); + } +#endif + } + + fclose(rgsFile); + } +} + +// Load style from a palette values array +RAYGUIDEF void GuiLoadStyleProps(const int *props, int count) +{ + int completeSets = count/(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); + int uncompleteSetProps = count%(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); + + // Load style palette values from array (complete property sets) + for (int i = 0; i < completeSets; i++) + { + for (int j = 0; j < (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); i++) GuiSetStyle(i, j, props[i]); + } + + // Load style palette values from array (uncomplete property set) + for (int k = 0; k < uncompleteSetProps; k++) GuiSetStyle(completeSets, k, props[completeSets*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + k]); +} + +// Load style default over global style +RAYGUIDEF void GuiLoadStyleDefault(void) +{ + // We set this variable first to avoid cyclic function calls + // when calling GuiSetStyle() and GuiGetStyle() + guiStyleLoaded = true; + + // Initialize default LIGHT style property values + GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); + GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); + GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff); + GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff); + GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff); + GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff); + GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff); + GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff); + GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); + GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); + GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); + GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); + GuiSetStyle(DEFAULT, INNER_PADDING, 1); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); + + // Populate all controls with default style + for (int i = 1; i < NUM_CONTROLS; i++) + { + for (int j = 0; j < NUM_PROPS_DEFAULT; j++) GuiSetStyle(i, j, GuiGetStyle(DEFAULT, j)); + } + + // Initialize extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); + GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property + + GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 2); + GuiSetStyle(BUTTON, INNER_PADDING, 4); + GuiSetStyle(TOGGLE, GROUP_PADDING, 2); + GuiSetStyle(SLIDER, SLIDER_WIDTH, 15); + GuiSetStyle(SLIDER, TEXT_PADDING, 5); + GuiSetStyle(CHECKBOX, CHECK_TEXT_PADDING, 5); + GuiSetStyle(COMBOBOX, SELECTOR_WIDTH, 30); + GuiSetStyle(COMBOBOX, SELECTOR_PADDING, 2); + GuiSetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING, 16); + GuiSetStyle(TEXTBOX, INNER_PADDING, 4); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT); + GuiSetStyle(TEXTBOX, MULTILINE_PADDING, 5); + GuiSetStyle(TEXTBOX, SPINNER_BUTTON_WIDTH, 20); // SPINNER specific property + GuiSetStyle(TEXTBOX, SPINNER_BUTTON_PADDING, 2); // SPINNER specific property + GuiSetStyle(TEXTBOX, SPINNER_BUTTON_BORDER_WIDTH, 1); // SPINNER specific property + //GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER); // TODO. + GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 6); + GuiSetStyle(COLORPICKER, BAR_WIDTH, 0x14); + GuiSetStyle(COLORPICKER, BAR_PADDING, 0xa); + GuiSetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT, 6); + GuiSetStyle(COLORPICKER, BAR_SELECTOR_PADDING, 2); + GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, 0x1e); + GuiSetStyle(LISTVIEW, ELEMENTS_PADDING, 2); + GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 10); + GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); + GuiSetStyle(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 +RAYGUIDEF void GuiUpdateStyleComplete(void) +{ + // Populate all controls with default style + // NOTE: Extended style properties are ignored + for (int i = 1; i < NUM_CONTROLS; i++) + { + for (int j = 0; j < NUM_PROPS_DEFAULT; j++) GuiSetStyle(i, j, GuiGetStyle(DEFAULT, j)); + } +} + +// Get text with icon id prepended +// NOTE: Useful to add icons by name id (enum) instead of +// a number that can change between ricon versions +RAYGUIDEF const char *GuiIconText(int iconId, const char *text) +{ + static char buffer[1024] = { 0 }; + memset(buffer, 0, 1024); + + sprintf(buffer, "#%03i#", iconId); + + if (text != NULL) + { + for (int i = 5; i < 1024; i++) + { + buffer[i] = text[i - 5]; + if (text[i - 5] == '\0') break; + } + } + + return buffer; +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// Split controls text into multiple strings +// Also check for multiple columns (required by GuiToggleGroup()) +static const char **GuiTextSplit(const char *text, int *count, int *textRow) +{ + // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) + // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, + // all used memory is static... it has some limitations: + // 1. Maximum number of possible split strings is set by MAX_SUBSTRINGS_COUNT + // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH + + #define MAX_TEXT_BUFFER_LENGTH 1024 + #define MAX_SUBSTRINGS_COUNT 64 + + static const char *result[MAX_SUBSTRINGS_COUNT] = { NULL }; + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); + + result[0] = buffer; + int counter = 1; + + if (textRow != NULL) textRow[0] = 0; + + // Count how many substrings we have on text and point to every one + for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + { + buffer[i] = text[i]; + if (buffer[i] == '\0') break; + else if ((buffer[i] == ';') || (buffer[i] == '\n')) + { + result[counter] = buffer + i + 1; + + if (textRow != NULL) + { + if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1; + else textRow[counter] = textRow[counter - 1]; + } + + buffer[i] = '\0'; // Set an end of string at this point + + counter++; + if (counter == MAX_SUBSTRINGS_COUNT) break; + } + } + + *count = counter; + + return result; +} + +// Convert color data from RGB to HSV +// NOTE: Color data should be passed normalized +static Vector3 ConvertRGBtoHSV(Vector3 rgb) +{ + Vector3 hsv = { 0.0f }; + float min = 0.0f; + float max = 0.0f; + float delta = 0.0f; + + min = (rgb.x < rgb.y)? rgb.x : rgb.y; + min = (min < rgb.z)? min : rgb.z; + + max = (rgb.x > rgb.y)? rgb.x : rgb.y; + max = (max > rgb.z)? max : rgb.z; + + hsv.z = max; // Value + delta = max - min; + + if (delta < 0.00001f) + { + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + if (max > 0.0f) + { + // NOTE: If max is 0, this divide would cause a crash + hsv.y = (delta/max); // Saturation + } + else + { + // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined + hsv.y = 0.0f; + hsv.x = 0.0f; // Undefined, maybe NAN? + return hsv; + } + + // NOTE: Comparing float values could not work properly + if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta + else + { + if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow + else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan + } + + hsv.x *= 60.0f; // Convert to degrees + + if (hsv.x < 0.0f) hsv.x += 360.0f; + + return hsv; +} + +// Convert color data from HSV to RGB +// NOTE: Color data should be passed normalized +static Vector3 ConvertHSVtoRGB(Vector3 hsv) +{ + Vector3 rgb = { 0.0f }; + float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f; + long i = 0; + + // NOTE: Comparing float values could not work properly + if (hsv.y <= 0.0f) + { + rgb.x = hsv.z; + rgb.y = hsv.z; + rgb.z = hsv.z; + return rgb; + } + + hh = hsv.x; + if (hh >= 360.0f) hh = 0.0f; + hh /= 60.0f; + + i = (long)hh; + ff = hh - i; + p = hsv.z*(1.0f - hsv.y); + q = hsv.z*(1.0f - (hsv.y*ff)); + t = hsv.z*(1.0f - (hsv.y*(1.0f - ff))); + + switch (i) + { + case 0: + { + rgb.x = hsv.z; + rgb.y = t; + rgb.z = p; + } break; + case 1: + { + rgb.x = q; + rgb.y = hsv.z; + rgb.z = p; + } break; + case 2: + { + rgb.x = p; + rgb.y = hsv.z; + rgb.z = t; + } break; + case 3: + { + rgb.x = p; + rgb.y = q; + rgb.z = hsv.z; + } break; + case 4: + { + rgb.x = t; + rgb.y = p; + rgb.z = hsv.z; + } break; + case 5: + default: + { + rgb.x = hsv.z; + rgb.y = p; + rgb.z = q; + } break; + } + + return rgb; +} + +#if defined(RAYGUI_STANDALONE) +// Returns a Color struct from hexadecimal value +static Color GetColor(int hexValue) +{ + Color color; + + color.r = (unsigned char)(hexValue >> 24) & 0xFF; + color.g = (unsigned char)(hexValue >> 16) & 0xFF; + color.b = (unsigned char)(hexValue >> 8) & 0xFF; + color.a = (unsigned char)hexValue & 0xFF; + + return color; +} + +// Returns hexadecimal value for a Color +static int ColorToInt(Color color) +{ + return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a); +} + +// Check if point is inside rectangle +static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) +{ + bool collision = false; + + if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && + (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true; + + return collision; +} + +// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +static Color Fade(Color color, float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)}; +} + +// Formatting of text with variables to 'embed' +static const char *TextFormat(const char *text, ...) +{ + #define MAX_FORMATTEXT_LENGTH 64 + + static char buffer[MAX_FORMATTEXT_LENGTH]; + + va_list args; + va_start(args, text); + vsprintf(buffer, text, args); + va_end(args); + + return buffer; +} +#endif // RAYGUI_STANDALONE + +#endif // RAYGUI_IMPLEMENTATION diff --git a/examples/shapes/shapes_bouncing_ball.c b/examples/shapes/shapes_bouncing_ball.c new file mode 100644 index 000000000..6b27c9dd3 --- /dev/null +++ b/examples/shapes/shapes_bouncing_ball.c @@ -0,0 +1,76 @@ +/******************************************************************************************* +* +* raylib [shapes] example - bouncing ball +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //--------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bouncing ball"); + + Vector2 ballPosition = { GetScreenWidth()/2, GetScreenHeight()/2 }; + Vector2 ballSpeed = { 5.0f, 4.0f }; + int ballRadius = 20; + + bool pause = 0; + int framesCounter = 0; + + SetTargetFPS(60); + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + if (IsKeyPressed(KEY_SPACE)) pause = !pause; + + if (!pause) + { + ballPosition.x += ballSpeed.x; + ballPosition.y += ballSpeed.y; + + // Check walls collision for bouncing + if ((ballPosition.x >= (GetScreenWidth() - ballRadius)) || (ballPosition.x <= ballRadius)) ballSpeed.x *= -1.0f; + if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -1.0f; + } + else framesCounter++; + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawCircleV(ballPosition, ballRadius, MAROON); + DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY); + + // On pause, we draw a blinking message + if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY); + + DrawFPS(10, 10); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_bouncing_ball.png b/examples/shapes/shapes_bouncing_ball.png new file mode 100644 index 0000000000000000000000000000000000000000..9d98e3a889d13b73e276354be935e906f5f63143 GIT binary patch literal 15523 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ#=yWJp1k%11A|SLr;B4qMO^ZqUteF> zw*?wVF)dcaL6mSXF1r{Y$J=;OKmselSk-zUgFVG$ZExFw42~Wwgh5Z4gPZU{?2*GaR-HNW5+6QQL;o4Z9$2*oe~&ASYbL;l7@>10Ntx2pLc(Fp4GZ zJ+k*wm&Vfg!~Km{C$5?|;o*^4?8faOY^G@;lzKbR{xI(@`l4-HA!s3-lCM<5w30R1R zJVk?BjFP@NxDya}<3+v`8A;N(T)!ZiW5OQxmJ9cIzt{>tdH3~zDAST#{KoAuKe1iVWpT4sSlpS5-3B-3hCD@wFLDAw->rDQ?iE~IZS7Y45W6=5 z?7_YazKUJCrwtUfA75Wz|KI&^H7@HCOjuqBf+O-}4lXrRh)7T#U52xqXnZIRcEZZ4 zQ70%Y9t{O>xiXp-K&9(wD2#@}Xz2)QUyg>tXehwC8KY@oG%bv#1>|1SXpR`o5me0) zjMFY|z!=(L%uy@|I?kDYU`G5+bEyJo$KIic!=Uc(udn|zc3=#$FlexFlv)+EUdl*M zG5HG~0uqpBS}Z%^;ua(1kp*QNmJ@GFddl3~HlG9aXBjLN4R*1onA`hj9KM& z4vwDO#_qx|LFx{C-~^8lT|pVAP&QzBan(~Ic9!X^z2L!#3(g58Y$+zbFoVEaIk*|C z&IRC4Kp!UWbf~IXNXL zSTil2JKjQ ze0qah@V5+(o;P4$pJhbxHPk_1u@Z=nagXF+8vtND<&R z!2@!akAWNkA5eZEu)x(l5oGUDuq_`TN$eMLOg3H=XlQ5HX{@w(Zr?>06OhdgIp8AV z@+lO<+8C}0C|r@0d|T3^HV2e07>gh&J`Zd*HsXaC)8c9eH?*<_6tU14AF`PSDTNGF zz8!S}N*;3_umY6Y5z&xpvc?@=f4q=p+Pime$i@vjc1Yy$tv&0qty|)_^6vI|@0O?} ztbFPhbU4f8KdkNJ0hur|u?J;iw2BqXC2G#l$LlhmnUdD^{W74J*WBN9OME+pTo<%tdcz_sy``n+wo+j=QKO zA;MyQ~5=KK|G!$S3<7iqKO$(!G0lAVK%@LzHVl+pL)Eu!Onqz}N_T{Iir+XGn zS+`Cv;%~uqcZ~qI!@Q;~2``&&6ex$;ZY?_Fa8z8e{?HxMfS*|#mVIvY@_+HsF{^5s z$=r#r-#^UKU;2|-tp1B$zT0gp*aT4!Wa-IUd=rWh9$PzaOpGci&Rohr{r5@PlZ&o$ zNUXinob6J);>hKMv#-DK*%uv7T(Pt`Yx(s?Kl_$6Cyuig6K0>&_-elMW!trXOT%g& zeUe{k9L}>CJWmQH8q^dGKyoZ- zxqSQX;+0t`Ee(3N;>?}?ER@q?etQ4n%N%#xH?Q!2Ui=hT@0ld|6jaNoaZb_PDb#{iwhRD=aK#%y#6H{+`L zO|7S&?r=-s?A=Yu4-% zmF$dVpDtd`aPwY%Q9$B{zy;M$U)Da93V-oW-gh!Ww=-mcP4FAIFA0a9^T2yX zMH6ACS1$vnzpU{RUpPZ5Yx(aBR)0KVnfp%LzIev-HF%0gsr}M_s;2RR(MlJs // Required for abs() + +int main() +{ + // Initialization + //--------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - collision area"); + + // Box A: Moving box + Rectangle boxA = { 10, GetScreenHeight()/2 - 50, 200, 100 }; + int boxASpeedX = 4; + + // Box B: Mouse moved box + Rectangle boxB = { GetScreenWidth()/2 - 30, GetScreenHeight()/2 - 30, 60, 60 }; + + Rectangle boxCollision = { 0 }; // Collision rectangle + + int screenUpperLimit = 40; // Top menu limits + + bool pause = false; // Movement pause + bool collision = false; // Collision detection + + SetTargetFPS(60); + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + // Move box if not paused + if (!pause) boxA.x += boxASpeedX; + + // Bounce box on x screen limits + if (((boxA.x + boxA.width) >= GetScreenWidth()) || (boxA.x <= 0)) boxASpeedX *= -1; + + // Update player-controlled-box (box02) + boxB.x = GetMouseX() - boxB.width/2; + boxB.y = GetMouseY() - boxB.height/2; + + // Make sure Box B does not go out of move area limits + if ((boxB.x + boxB.width) >= GetScreenWidth()) boxB.x = GetScreenWidth() - boxB.width; + else if (boxB.x <= 0) boxB.x = 0; + + if ((boxB.y + boxB.height) >= GetScreenHeight()) boxB.y = GetScreenHeight() - boxB.height; + else if (boxB.y <= screenUpperLimit) boxB.y = screenUpperLimit; + + // Check boxes collision + collision = CheckCollisionRecs(boxA, boxB); + + // Get collision rectangle (only on collision) + if (collision) boxCollision = GetCollisionRec(boxA, boxB); + + // Pause Box A movement + if (IsKeyPressed(KEY_SPACE)) pause = !pause; + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawRectangle(0, 0, screenWidth, screenUpperLimit, collision? RED : BLACK); + + DrawRectangleRec(boxA, GOLD); + DrawRectangleRec(boxB, BLUE); + + if (collision) + { + // Draw collision area + DrawRectangleRec(boxCollision, LIME); + + // Draw collision message + DrawText("COLLISION!", GetScreenWidth()/2 - MeasureText("COLLISION!", 20)/2, screenUpperLimit/2 - 10, 20, BLACK); + + // Draw collision area + DrawText(FormatText("Collision Area: %i", (int)boxCollision.width*(int)boxCollision.height), GetScreenWidth()/2 - 100, screenUpperLimit + 10, 20, BLACK); + } + + DrawFPS(10, 10); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_collision_area.png b/examples/shapes/shapes_collision_area.png new file mode 100644 index 0000000000000000000000000000000000000000..760e1bc1d94f9f11f21993fa5193a9ec8850ba2e GIT binary patch literal 15319 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ#=yWJp1k%11B3NEPZ!6Kin!#OWtsox zCyMm69mwFoPB6$iHDsDhad%EIVR6GsG{h+y++vjU&0ef%AjO1@;8`p?;i3x;$;FBW zZ=4cJ*iuXa`*Atrf-Z}jwZh`gT%2ymgSa6FoAnSe*$Ed-a691-#0jBPc0z*)Q_yvP zqlOO|3lPD3K@IF1&1^irIpDO$H)yGSN6@b|vzgcQg?w${QNLz0!*a>4=BQt8m)}97 z>wr_kmB26Qvz|@(By7Rt@1?Qmb@NBgX{!UOJwp5Q^tRsd)h_1z>ECsvK*IMSBpg7I zG^O>x3!W1hTvEJ^7X>7+5DTI?HcZG8&Pi*GsNHd8vejb7&HW+2JFm8%wtaHTd{!GY zIWWZib4WKUXysTpb5eTBtHq%wgsm?-z3Jhq`XMl@QzGY~;jBYYr3of1C%79gN=~>q z#Ta`kS*+-=T_8b{_cg*vjl7IO={#$)yynn9iXUZ4(b4vTJLFvVl``teufkq3@H-QCT>;$8J=a;CZnDF;P)Aa)mh{+as zOqQ@^%{JKx)4|Yw;Dx5|mtF=Z^X>ifyv(9hpvj0K*BP8=T45@oHbbQkWN=*2Wm!?j zCARBy`zyK5i+g1k?<@eNw}vQ1hc_z&B%VJ1IyuF%aYF42{r}>Jwz!h3ffUmh zNRlow!==VyyMV;oh90$Tu-HM5HpX482Qt`G5NRGGCHIs$xLw9sK^^!2DON&oI&*k9 z;eswpgxg_Qs%`V)C@3zT{N&2|`2CZfn$EC&@ykIsAHAYr@AqLKb&SUV)LNRcZlQYIoHs$Y!1F83#vb1 zWe}rOE5ky;qNx50!LM|K(q&F;?Y}Xz<)GDLgByWXC>WFr zT3-CmsDODB5l?UygDs?aH5Zly;WDsnO|XTRu!Y%Z78}?b*od}xh(llDaVUXeX4DCw zX3A(NjE2H!rbI5EMsvhyju_1mqd5Y7fNZo}7%dk@%Z1T$VT6_o45u?9z+FC`{Q?hm z9kZ2M3+_H!I6ats?fS(Hh)$}+0Rah6ueJ^>kBt}|(gO|3kA}i%D8MGDM$^J*S{O|W z$m67=Ibt+NP%%g7ES_kEoV>R1F&;bpt66=*^}h>uM(?}U6SjWw&MfFCIB2$6XwAhf zhR8aQ8!`lEn7~t(Qs9|stVA7T!ekX?MRB1Un;yYm@6h?N#SZ44GHZqpNb)mk7)40A~h z;Yse%(WKGQq|wnNl17sn@)SpxsDN?}q$nC)B?HPoqoFVw3b0aSG%bv#h0(NtJU=m- zBSv$?XpR`o5s((+XxkA~8jOa*XehwS#L=`cnifXW0&@L0nj`3*BN!OYGY4`BbWG7; QU|?YIboFyt=akR{0A+xrc>n+a literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_draw_circle_sector.c b/examples/shapes/shapes_draw_circle_sector.c new file mode 100644 index 000000000..a88a59d56 --- /dev/null +++ b/examples/shapes/shapes_draw_circle_sector.c @@ -0,0 +1,79 @@ +/******************************************************************************************* +* +* raylib [shapes] example - draw circle sector (with gui options) +* +* 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) +* +* Copyright (c) 2019 Vlad Adrian (@Demizdor) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include + +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for GUI controls + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw circle sector"); + + Vector2 center = {(GetScreenWidth() - 300)/2, GetScreenHeight()/2 }; + + float outerRadius = 180.f; + int startAngle = 0; + int endAngle = 180; + int segments = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // NOTE: All variables update happens inside GUI control functions + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f)); + DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); + + DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.3)); + DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.6)); + + // Draw GUI controls + //------------------------------------------------------------------------------ + startAngle = GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", startAngle, 0, 720, true ); + endAngle = GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", endAngle, 0, 720, true); + + outerRadius = GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", outerRadius, 0, 200, true); + segments = GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", segments, 0, 100, true); + //------------------------------------------------------------------------------ + + DrawText(FormatText("MODE: %s", (segments >= 4)? "MANUAL" : "AUTO"), 600, 200, 10, (segments >= 4)? MAROON : DARKGRAY); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_draw_circle_sector.png b/examples/shapes/shapes_draw_circle_sector.png new file mode 100644 index 0000000000000000000000000000000000000000..f1624b81e26ee263160dc7526d8af6074cb9cdc0 GIT binary patch literal 18474 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 + +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for GUI controls + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw rectangle rounded"); + + float roundness = 0.2f; + int width = 200; + int height = 100; + int segments = 0; + int lineThick = 1; + + bool drawRect = false; + bool drawRoundedRect = true; + bool drawRoundedLines = false; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + Rectangle rec = { (GetScreenWidth() - width - 250)/2, (GetScreenHeight() - height)/2, width, height }; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawLine(560, 0, 560, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f)); + DrawRectangle(560, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); + + if (drawRect) DrawRectangleRec(rec, Fade(GOLD, 0.6)); + if (drawRoundedRect) DrawRectangleRounded(rec, roundness, segments, Fade(MAROON, 0.2)); + if (drawRoundedLines) DrawRectangleRoundedLines(rec,roundness, segments, lineThick, Fade(MAROON, 0.4)); + + // Draw GUI controls + //------------------------------------------------------------------------------ + width = GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", width, 0, GetScreenWidth() - 300, true ); + height = GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", height, 0, GetScreenHeight() - 50, true); + roundness = GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", roundness, 0.0f, 1.0f, true); + lineThick = GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", lineThick, 0, 20, true); + segments = GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", segments, 0, 60, true); + + drawRoundedRect = GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", drawRoundedRect); + drawRoundedLines = GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", drawRoundedLines); + drawRect = GuiCheckBox((Rectangle){ 640, 380, 20, 20}, "DrawRect", drawRect); + //------------------------------------------------------------------------------ + + DrawText(FormatText("MODE: %s", (segments >= 4)? "MANUAL" : "AUTO"), 640, 280, 10, (segments >= 4)? MAROON : DARKGRAY); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shapes/shapes_draw_rectangle_rounded.png b/examples/shapes/shapes_draw_rectangle_rounded.png new file mode 100644 index 0000000000000000000000000000000000000000..20d1708fb2126aa7d199a1555bcf5a9d51766f04 GIT binary patch literal 17952 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 Oz~JfX=d#Wzp$Py(dN<+# literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_draw_ring.c b/examples/shapes/shapes_draw_ring.c new file mode 100644 index 000000000..d56b9c2eb --- /dev/null +++ b/examples/shapes/shapes_draw_ring.c @@ -0,0 +1,92 @@ +/******************************************************************************************* +* +* raylib [shapes] example - draw ring (with gui options) +* +* 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) +* +* Copyright (c) 2019 Vlad Adrian (@Demizdor) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include + +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for GUI controls + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw ring"); + + Vector2 center = {(GetScreenWidth() - 300)/2, GetScreenHeight()/2 }; + + float innerRadius = 80.0f; + float outerRadius = 190.0f; + + int startAngle = 0; + int endAngle = 360; + int segments = 0; + + bool drawRing = true; + bool drawRingLines = false; + bool drawCircleLines = false; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // NOTE: All variables update happens inside GUI control functions + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f)); + DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); + + if (drawRing) DrawRing(center, innerRadius, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.3)); + if (drawRingLines) DrawRingLines(center, innerRadius, outerRadius, startAngle, endAngle, segments, Fade(BLACK, 0.4)); + if (drawCircleLines) DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, Fade(BLACK, 0.4)); + + // Draw GUI controls + //------------------------------------------------------------------------------ + startAngle = GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", startAngle, -450, 450, true); + endAngle = GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", endAngle, -450, 450, true); + + innerRadius = GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", innerRadius, 0, 100, true); + outerRadius = GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", outerRadius, 0, 200, true); + + segments = GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", segments, 0, 100, true); + + drawRing = GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", drawRing); + drawRingLines = GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", drawRingLines); + drawCircleLines = GuiCheckBox((Rectangle){ 600, 380, 20, 20 }, "Draw CircleLines", drawCircleLines); + //------------------------------------------------------------------------------ + + DrawText(FormatText("MODE: %s", (segments >= 4)? "MANUAL" : "AUTO"), 600, 270, 10, (segments >= 4)? MAROON : DARKGRAY); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_draw_ring.png b/examples/shapes/shapes_draw_ring.png new file mode 100644 index 0000000000000000000000000000000000000000..f97734a2e4d1d92d45dd83cca935edad09a9a67b GIT binary patch literal 21873 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 oBRHBVkqmdKI;Vst05m@o%K!iX literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_easing_box_anim.c b/examples/shapes/shapes_easing_box_anim.c new file mode 100644 index 000000000..c74cb2a34 --- /dev/null +++ b/examples/shapes/shapes_easing_box_anim.c @@ -0,0 +1,136 @@ +/******************************************************************************************* +* +* raylib [shapes] example - easings box anim +* +* 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) +* +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include "easings.h" // Required for easing functions + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings box anim"); + + // Box variables to be animated with easings + Rectangle rec = { GetScreenWidth()/2, -100, 100, 100 }; + float rotation = 0.0f; + float alpha = 1.0f; + + int state = 0; + int framesCounter = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + switch (state) + { + case 0: // Move box down to center of screen + { + framesCounter++; + + // NOTE: Remember that 3rd parameter of easing function refers to + // desired value variation, do not confuse it with expected final value! + rec.y = EaseElasticOut(framesCounter, -100, GetScreenHeight()/2 + 100, 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 1; + } + } break; + case 1: // Scale box to an horizontal bar + { + framesCounter++; + rec.height = EaseBounceOut(framesCounter, 100, -90, 120); + rec.width = EaseBounceOut(framesCounter, 100, GetScreenWidth(), 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 2; + } + } break; + case 2: // Rotate horizontal bar rectangle + { + framesCounter++; + rotation = EaseQuadOut(framesCounter, 0.0f, 270.0f, 240); + + if (framesCounter >= 240) + { + framesCounter = 0; + state = 3; + } + } break; + case 3: // Increase bar size to fill all screen + { + framesCounter++; + rec.height = EaseCircOut(framesCounter, 10, GetScreenWidth(), 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 4; + } + } break; + case 4: // Fade out animation + { + framesCounter++; + alpha = EaseSineOut(framesCounter, 1.0f, -1.0f, 160); + + if (framesCounter >= 160) + { + framesCounter = 0; + state = 5; + } + } break; + default: break; + } + + // Reset animation at any moment + if (IsKeyPressed(KEY_SPACE)) + { + rec = (Rectangle){ GetScreenWidth()/2, -100, 100, 100 }; + rotation = 0.0f; + alpha = 1.0f; + state = 0; + framesCounter = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawRectanglePro(rec, (Vector2){ rec.width/2, rec.height/2 }, rotation, Fade(BLACK, alpha)); + + DrawText("PRESS [SPACE] TO RESET BOX ANIMATION!", 10, GetScreenHeight() - 25, 20, LIGHTGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_easing_box_anim.png b/examples/shapes/shapes_easing_box_anim.png new file mode 100644 index 0000000000000000000000000000000000000000..87982417f85f6547780a3d96cd7304f4d4f312b9 GIT binary patch literal 15217 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ#=yWJp1k%11B11Nr;B4qMO^ZqUteF> zw*?wVF)dcaL6mSXF1r{Y$J=;OKmselSk-zUgFVG$ZExFw42~Wwgh5Z4gPZU{?2*GaR-HNW5+6QQL;o4Z9$2*oe~&ASYbL;l7@>10Ntx2pM&POg9YOWlXef+^0<3v6nifXW!f0ARZtIWch|wG|nj=PY1bQEP zv|Jc17e>p4(Q;w5TmW~a(TLH~akO+CEgeTo$I;RetzZOmM@z>+Q#vxbEtvkV-|Y=J zUa%7vlv&)SC-g4cO#Uq01zi?5YlX#~xh5=b&I#CvEzCx<*l@_1u(ZW<^qgzxS@vph zJAvW!_4V=Y&DGdE2o-xF#I)F0VKFDpkZ8zLG`Phm>6?Rl=9athBHxLOBx$VEwxIdk zw+%hZKs0s*55>VwSXl*i5=N+xIsp_iqoFVw3b4{@G%bv#h0(NtTv3eXh|wG|nj=PY z1bSp4(Q;w5T);NWF#yQ_xkI&s=DB2`@DOez3f%fUmtlEoVBz2)8%);g+-OnULFvs8Vn z+n#ha&Dqgu~E#W{|-mR_g)hG)x)r^#6Qns!fd{dyT2Xs>{PGU6#X@h zcwG_I{yF1z8r)k9kl8NHM7RiAiPw;)=psj2C%Oc=OLd0U&pac2YNhET z_jT5Z4_A9H4&JxIcF{`fUFIw@sm->3gJ-SFh)ojoa=#L_+kcJalUwd<>^Y_`zVb&d%h6@Yi=5k8rZiQ}s zBr4PN#P6^BYcF+g3wPt`lzH1sgC3`t>~Sl8j1lWR?;$DfA>I@xIpN|FGkn`OL5X%~ gPgO36=3roG5}zPgJoAq<0|Nttr>mdKI;Vst0H&mOsQ>@~ literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_easings_ball_anim.c b/examples/shapes/shapes_easings_ball_anim.c new file mode 100644 index 000000000..4477203c6 --- /dev/null +++ b/examples/shapes/shapes_easings_ball_anim.c @@ -0,0 +1,110 @@ +/******************************************************************************************* +* +* raylib [shapes] example - easings ball anim +* +* 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) +* +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include "easings.h" // Required for easing functions + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings ball anim"); + + // Ball variable value to be animated with easings + int ballPositionX = -100; + int ballRadius = 20; + float ballAlpha = 0.0f; + + int state = 0; + int framesCounter = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (state == 0) // Move ball position X with easing + { + framesCounter++; + ballPositionX = EaseElasticOut(framesCounter, -100, screenWidth/2 + 100, 120); + + if (framesCounter >= 120) + { + framesCounter = 0; + state = 1; + } + } + else if (state == 1) // Increase ball radius with easing + { + framesCounter++; + ballRadius = EaseElasticIn(framesCounter, 20, 500, 200); + + if (framesCounter >= 200) + { + framesCounter = 0; + state = 2; + } + } + else if (state == 2) // Change ball alpha with easing (background color blending) + { + framesCounter++; + ballAlpha = EaseCubicOut(framesCounter, 0.0f, 1.0f, 200); + + if (framesCounter >= 200) + { + framesCounter = 0; + state = 3; + } + } + else if (state == 3) // Reset state to play again + { + if (IsKeyPressed(KEY_ENTER)) + { + // Reset required variables to play again + ballPositionX = -100; + ballRadius = 20; + ballAlpha = 0.0f; + state = 0; + } + } + + if (IsKeyPressed(KEY_R)) framesCounter = 0; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (state >= 2) DrawRectangle(0, 0, screenWidth, screenHeight, GREEN); + DrawCircle(ballPositionX, 200, ballRadius, Fade(RED, 1.0f - ballAlpha)); + + if (state == 3) DrawText("PRESS [ENTER] TO PLAY AGAIN!", 240, 200, 20, BLACK); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_easings_ball_anim.png b/examples/shapes/shapes_easings_ball_anim.png new file mode 100644 index 0000000000000000000000000000000000000000..1668b136569ee943143763b92f2adc9993e25cdf GIT binary patch literal 15602 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 gZo4__n=&v&uwSn9XS&FVdQ&MBb@065;*cmMzZ literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_easings_rectangle_array.c b/examples/shapes/shapes_easings_rectangle_array.c new file mode 100644 index 000000000..27bad5c94 --- /dev/null +++ b/examples/shapes/shapes_easings_rectangle_array.c @@ -0,0 +1,118 @@ +/******************************************************************************************* +* +* raylib [shapes] example - easings rectangle array +* +* NOTE: This example requires 'easings.h' library, provided on raylib/src. Just copy +* the library to same directory as example or make sure it's available on include path. +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include "easings.h" // Required for easing functions + +#define RECS_WIDTH 50 +#define RECS_HEIGHT 50 + +#define MAX_RECS_X 800/RECS_WIDTH +#define MAX_RECS_Y 450/RECS_HEIGHT + +#define PLAY_TIME_IN_FRAMES 240 // At 60 fps = 4 seconds + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings rectangle array"); + + Rectangle recs[MAX_RECS_X*MAX_RECS_Y]; + + for (int y = 0; y < MAX_RECS_Y; y++) + { + for (int x = 0; x < MAX_RECS_X; x++) + { + recs[y*MAX_RECS_X + x].x = RECS_WIDTH/2 + RECS_WIDTH*x; + recs[y*MAX_RECS_X + x].y = RECS_HEIGHT/2 + RECS_HEIGHT*y; + recs[y*MAX_RECS_X + x].width = RECS_WIDTH; + recs[y*MAX_RECS_X + x].height = RECS_HEIGHT; + } + } + + float rotation = 0.0f; + int framesCounter = 0; + int state = 0; // Rectangles animation state: 0-Playing, 1-Finished + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (state == 0) + { + framesCounter++; + + for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++) + { + recs[i].height = EaseCircOut(framesCounter, RECS_HEIGHT, -RECS_HEIGHT, PLAY_TIME_IN_FRAMES); + recs[i].width = EaseCircOut(framesCounter, RECS_WIDTH, -RECS_WIDTH, PLAY_TIME_IN_FRAMES); + + if (recs[i].height < 0) recs[i].height = 0; + if (recs[i].width < 0) recs[i].width = 0; + + if ((recs[i].height == 0) && (recs[i].width == 0)) state = 1; // Finish playing + + rotation = EaseLinearIn(framesCounter, 0.0f, 360.0f, PLAY_TIME_IN_FRAMES); + } + } + else if ((state == 1) && IsKeyPressed(KEY_SPACE)) + { + // When animation has finished, press space to restart + framesCounter = 0; + + for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++) + { + recs[i].height = RECS_HEIGHT; + recs[i].width = RECS_WIDTH; + } + + state = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (state == 0) + { + for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++) + { + DrawRectanglePro(recs[i], (Vector2){ recs[i].width/2, recs[i].height/2 }, rotation, RED); + } + } + else if (state == 1) DrawText("PRESS [SPACE] TO PLAY AGAIN!", 240, 200, 20, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_easings_rectangle_array.png b/examples/shapes/shapes_easings_rectangle_array.png new file mode 100644 index 0000000000000000000000000000000000000000..661290e65655bdb1a62b2026f90ed164c595ef69 GIT binary patch literal 17142 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 literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_following_eyes.c b/examples/shapes/shapes_following_eyes.c new file mode 100644 index 000000000..e99a7e07e --- /dev/null +++ b/examples/shapes/shapes_following_eyes.c @@ -0,0 +1,104 @@ +/******************************************************************************************* +* +* raylib [shapes] example - following eyes +* +* 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) +* +* Copyright (c) 2013-2019 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include // Required for: atan2f() + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - following eyes"); + + Vector2 scleraLeftPosition = { GetScreenWidth()/2 - 100, GetScreenHeight()/2 }; + Vector2 scleraRightPosition = { GetScreenWidth()/2 + 100, GetScreenHeight()/2 }; + float scleraRadius = 80; + + Vector2 irisLeftPosition = { GetScreenWidth()/2 - 100, GetScreenHeight()/2 }; + Vector2 irisRightPosition = { GetScreenWidth()/2 + 100, GetScreenHeight()/2}; + float irisRadius = 24; + + float angle; + float dx, dy, dxx, dyy; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + irisLeftPosition = GetMousePosition(); + irisRightPosition = GetMousePosition(); + + // Check not inside the left eye sclera + if (!CheckCollisionPointCircle(irisLeftPosition, scleraLeftPosition, scleraRadius - 20)) + { + dx = irisLeftPosition.x - scleraLeftPosition.x; + dy = irisLeftPosition.y - scleraLeftPosition.y; + + angle = atan2f(dy, dx); + + dxx = (scleraRadius - irisRadius)*cosf(angle); + dyy = (scleraRadius - irisRadius)*sinf(angle); + + irisLeftPosition.x = scleraLeftPosition.x + dxx; + irisLeftPosition.y = scleraLeftPosition.y + dyy; + } + + // Check not inside the right eye sclera + if (!CheckCollisionPointCircle(irisRightPosition, scleraRightPosition, scleraRadius - 20)) + { + dx = irisRightPosition.x - scleraRightPosition.x; + dy = irisRightPosition.y - scleraRightPosition.y; + + angle = atan2f(dy, dx); + + dxx = (scleraRadius - irisRadius)*cosf(angle); + dyy = (scleraRadius - irisRadius)*sinf(angle); + + irisRightPosition.x = scleraRightPosition.x + dxx; + irisRightPosition.y = scleraRightPosition.y + dyy; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawCircleV(scleraLeftPosition, scleraRadius, LIGHTGRAY); + DrawCircleV(irisLeftPosition, irisRadius, BROWN); + DrawCircleV(irisLeftPosition, 10, BLACK); + + DrawCircleV(scleraRightPosition, scleraRadius, LIGHTGRAY); + DrawCircleV(irisRightPosition, irisRadius, DARKGREEN); + DrawCircleV(irisRightPosition, 10, BLACK); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_following_eyes.png b/examples/shapes/shapes_following_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..7a2bc9ce6cc5866b8c3ef4f0469ecda9f47ae5a6 GIT binary patch literal 16169 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