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 00000000..810aeccb --- /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 00000000..3277d7ea --- /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 00000000..6b27c9dd --- /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 zcmeHOeN+=y7N3L^q67`%N`eIv5mt9uWKF1+Z-FKt1yi8arCOq;5CSR-5q8l^KnQ-s z0xezGxVYjN6bg!hF3_cbph6Lr0RctPvWgo%1go(16FMIRCYpfTdQQ)tGylxtOon;) z{_cIhci->6*(dPl8_hMF3jlzTkGEF<0O+v*fOMX!58pBUxVQlToW(v~Yl8mt(Z|o8 zj14Br-m^C&gKQla36+!&2`r3~AUZ?qg_Mw5BQgl(79W(=h^TTc7khb}JI|z6`;YKH zlo%#K%nZoOMVA@w|DZGON#!=@blOmf*>GB?4TuTtI_!f@xR03-^oyE6TC}CAzN&5i zGG$0zdQwMb;o*$ldW8WeEht{#drM@))QUeGm>P33=v%@Iyrc_apvOa+8gxj=F{~e` zQy#v;G|Kt(SogXj*NwyMu5Tc&vo7SNOftw}fdyN*6rQJ0%SF14ZQo5|`bFtZAQ;+t z4WccJby zw2DdhIKC$DUkft|4{xw+A6RHBdAjdxS~tfTj7Zet4(y^VmMU+N%JWBlda||tj>2zUUONMw%Y>*R@~UQ;ZW5(pFE8AYIg@Ctzffdat`NM%K!K%k&5u!&qC za)HPN{C%`>2~m3U>r%9+w|Pa==w@k(2e{Veb{+rAQ( zj(RjyBkDYoINsHLjgKLRZ_Xdhz2b2$viu3I9U-^JcMO>D-~C@D^ds| zGomGYl!}^A_)_MpwF~W$``IQ)&(Q6w&unki?-j;f7geHy*paQ6+Ejvv0H0E%pCoMp zeQbM1w&Ov&YBRrUa6r#sQ^P=GYT)y7R>=y5T>k~gPneMRL^QUm9()B&N~ex+U^Jhi zq|^vSkJ95@d8OURh%i|o@#+5`vl*Fw;VfRb8?W>l`Wjm-%$qgTBmtG?#TS-6{Jr7= zYHEfjYPW@k-6^SnixUORO^1dYko=ZfkmVuOyPvz6*}jnbRve1|z-p-eHfWv>y@@^i zTTFJmh(}NU9sS5V{LRR?N_43ESc*NtM-t91X&O+$Spw#M26=Ijk=>(EifI2<<7G0S zlg_%rWWrQw^EZ@`Vz7+grr`$Oan&L+xDyuHHM!ehNGsyC zpdkH(TLdpjlK`t0ldGz#f=-u|mov7_D{l5WcYtxp>g%+_!^f5i3;O&6>crd$b?xJg zHH^4!_yrQr9d|TfUYqq8wKHp#fVM@AbS}BHYRrU=8BGYpc?2(>we%yM zeX_D|mi}=xrVgrC7sPue;ls*hEf~HMe3Pr-O`Qh8D^@yYbhyrEX2nfgm8h<~;PsN& z>JwXib=gGZ0+9C+vI}+?j zup_~a1Un*3NT5KVKzyozepE$#utD@A5JH5zStazy_P(ZUpO~zZC+#*okM80x6Y%Pc z98!e+$ycMTK5#i3bJ_iZDLe7}4F|zvaY_Eeq_yi*->(rT)#J73pnd(pt_w4_d;#h_Q-jZ zl9`T8*n*qk56q6G0L5W@qbo?CQkXrvw{(@YU+)16#GxrtyY7!xc|6Or_>ErQE$FWY zpaq{ywu@!!v6DM0ZHozV4?ejAG;&zkKF&slcTIx(t;#yH1>S9uUb#0i6B5!^RV+_$ zJ3CxHWF2=mxlJP7HW581KBwB-?>||?#w-WdMx&A1fc4)gn2e$wOy%P*H%@#WIU+E; z1-*57s=o(Jb}`v%uwUv%OAJYce@{+r>MQ37=Nv5i+;M?#QNn2E>XUMC`x{=YfUrG3byWEG~1@c@>uEhMh;I2Tt;`F%X zR?q3CCT$pN2sp#Qz>{#|QL^K#6@$Dg)3KJ(tB_gRyR>Mm_8Fw1*~ zn{wRRA;kE@C6JQ^^%`I7nAdrenviN*!D%eppD6w^t^Q)2>fHuWWQ01VVOUIW%oNkx zU8h1O(q%vBvJW=0)e1W#gq~D-!Tc#7{x8q4P0y}ZI)VWJ-pTyEp@-}u;E#_1pLhJd JPI?HFe*~UJXl(!h literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_collision_area.c b/examples/shapes/shapes_collision_area.c new file mode 100644 index 00000000..e61623ab --- /dev/null +++ b/examples/shapes/shapes_collision_area.c @@ -0,0 +1,108 @@ +/******************************************************************************************* +* +* raylib [shapes] example - collision area +* +* 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 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_8XZ1{4ubUVDOp!Fryji(^PdTyo8_%zyI} zMS9u}WN=_77-XFqGEJtqJ13a1xM3w4;uH;TF-rPoFIF^=V!}r7ES8;c(FKPjP{SLi zgc7zClfZsl&bXk<;%2R|xHA{08}cA-$iZekL=5Nz6WmVt193tqm7UNa!W4Af->Bh3 z#sWm}UQh%3Ml&0aZw@%E@eNvP-x2g{&1~j1eIZ|4c+{`i%&=Utt2yeI+vRuA=sMuk za3%0d`mARYJ_%be`Fm+BdfohybK2^FYLC$VJiV=Ve6@=?fBJVFDUk4ehzOf0tp{H4 zoXFsk;%&SrAc2Kg5Y4e+LY8n&T4O}*jw_R`7Bg<{5Bc4Bwf(g1lUwGq+MvmSA?}|; zx>-Rh$GVx5(ofmxjrIS&nI9YUm>6Wom#B_~{*VvIeNELL>b zE|4I}`x@b-Mqb9CbRM_I-CG1C?mjl0l>|$H1rQH($zX9i*grrIOt4{g%6QMV_Pfu7 zZ2O(nek*EcUC+2B2i5UVj_Js}f3E9i$`|=_O8c%v1kM&th%-&`ICH@*&4?%G3?{q^ zj+)!l`i?K@?ZWB%-9I0JMhnk3fdyae1fzcEm#C$f@b^N~^#cxw$rgA_mat{bHrWW% z!O(u-g{JS9UIr)g?fvt-%%W7F$%rA>8JuTYVJb25FEG!nsN)jbb-MkPT<68TvWs^X zfYMt-l%m6%l>rh@pMRa4V%a#M_J#7FBdgrQ+wF>-b?YC^5>&cg6Vup{t(H>amSyrD znjeU=PNGrS;lKOgYFx=0Sc1F|WLj*ju=r&TE;S7VGja(q74e?&zd&(T#F5|4A4t#(VDShheF<&5NU;xOnoDE9>L; zPkL%P!}i55Pf_)2MKRC#LiSwHp2Hr!%lW&x&c9C4<4@&do(TlmM;XD2iUMHH0hYsC z@CMjiV2uu`O`Y{vIL_uwuxj$pWSC%mnQ8O%pubzX4jdFax5aP8l5-Ukr0sTmxoCa2 zPplSJhz2McSTP&T`UWemF|yhVF>oe?R_Hfc8P@HUZn5!NYpZ;uFJ*E6j>)^j8$y0( zh(3JsYxa$bg(flXn|~qF1ySWF!UM``EHCzcn|DW2G@@m~tGKM+{da6Rm;9O?Q9gOs z^QKE5)TbD_eb4C;n+J8iLmWrXIbazw2Q{H0iWE?i1=XLhGKf*Cm0_V^QB?nh;8(gq z=`ts__TQMeH4=PSUF>wRTZ;RoWuv@$#q;`kf13?+qD-iPZP}2Zc#9_}7?cZIUi{CffO!)UPjD84Eu?uh z7nTI!GO%n-u!Wbf1?UYnoC&He9^%kfcpOTgm>G2fsF^Yv3ZtPgnkkXXr_mfSnj=PY z#AuE{A0Qho7e>p4(Q;w5To|F{0>kNy2ymB=XTQLMUB_&t)`Gjw7ETXlU%P&B1EP~E zaX>%<)T^z7jSgURe@BP(Kr<(!p)eW>unDTsv@n_$M$-avMKPKqMsoxebA-;~iB`zT zYYQLavD3er)hAs4yKrapzH2>U>lg3Lf{ucNW}Ai9T-;)atOL0BjofN=n@ss z7%ZeH8eJs=3X0KC7!8Hd9DzJ0J(?p%bHr$l7|jvL9hT7?F`6SrbHr$lK=d9EtkL$u tXz4gwI*yi(qopIFOhmBgP&zU&oM#T?66lzs0o<_5;OXk;vd$@?2>@k)q + +#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 zcmeHvc~p~E_ihqW2of|(wIP6zqNuG{z#xi318BejrD|JBwFNeQ4a^(LA1$ACJdBD00R(FQsm;secJ6zre>}c- zb)oi+Aeq1@xBEzoIe)ofi#R7I#~9q-`diG)!V=YmOodPtll=S-OJ{-6M{lm|_snij z|7i+3&G(EKIvn-%M;u?>N2tnBsyyy-BAwRsoDbgF=;TVL5JS_y)6j*|Q1h=eP!gQ} zorKpY31j}>l90IMUZ)4cUfsBAf&1&uZ^eF=9j*14)b&U2(PyIq=Ns3iJzxH+7~UL_ zuFnBkywAV#r*`9a89gPhuD9fPY@AO;Us=$TC13lVoqw@v@wg|dhFi}OI=u*9x=43S zqbpx#mPp-mpYy@5T4dr}(iC46KKJIZNqwMDHPFwvbjrU`ubC=W-svido)^aL&Bz<~ z)H@bF)@ ztYKR|_#d{+HMg(jCU`wp3Xn|cyCU6eDDf}+R|KP9%nPKXO2A4 zA=m7wH;)aqw-Ea^%5Nme#D4!a-|G4dF;((gt(V6*M@H^5_w=^@ChsUP_VqKm&iWt3 zPmN=;{ULUsAQPzF{x`f!uR}$irycCOmX|0A71{qK!O!?VO2HH&Ykb}{oN&)QwJW38 z`EpSE+!H5Gm>+KTqegW!;~ygBB!a0^ry5(r#WBezzZh@4JuHPP=F0kiC}zE%@YvCH zh1yqxgu1Su$lMp3B}&&^a|E-QcFou{asMa#yPXpE z8#?2~h-LSN7&HHgWLvEOu8;}x{e=2~UOPtS&rLIxE?KwE^HR?n((9$V^tA+k_@iS= z%gXqlchUD3kLmPT(~qW%9s_P1nb^!_QH1h~ppvObR|qXqK~uQQ@2bo245xP!Br|{E zTqY{qC{Rf}1Wsm6>^C1&dITHZ%JdXid{T0JCXe%(c2Th`aBnB$w4OD02(GU< zkK2TBZ_6WLO^;2}b{siOHrtP<7RiH3){Ju3)*kvfDz|RE>0@=_>^Rk~UpbNZro?n| zdhD9&BhyT~*1JUrXH*a{-mN{J^j1%z_Gn_vB;6d_xk)b92 zFGZYD`m8V?tL0|H@LN0un~urOU+u5&6kQPB@1nNf*Jr{E&5Pd`=?(ou-OV zC4Le??S>-rG3V>9xX4{N@|s{#bUj17?Y)?BBgWtZ)abh5Emcc-O?lR@qv%E(>DjEMQpm_H7s`!B8@9V*36-SHy?Q&5H0Q7D)!s zQ^{VLV7(Uff#aQ?Sxor5^1IV>GH1};Na*PdeJEg?ttsm&xY@xwLwa^=q@lk$aDR-J2zndK!eoNhc9h&CFITx=S5MRQzQ*;r@uPFp?=*5N zeBE<9cBLL5Gg+!NeKG!9)rHjuL5)TiAg?MgS*~9)x=!h~h_}^4aObtH{vqLBA%^>q z#zn1Zz=wT5%Wj5+nJ)r{t7+g532;7wQdLQ5{v}RXtTioh<2OwfZFP#2{rQZi(z6)K zRe^A!``gB)lN3U!RG$%}S~Ib*e2Kl0P;=Ck3k(H3uFIcpUy5?Hk|ADAx=W#$BE)X&0;BwKelT=MA{e z7MYH}^hNtuBWIMn({#LN@MCS#WD?rl?eJ!Fvij)HTr?;+9+TW!+iV`&S0<=g%Mlxv2kJlP+@GtIe3J@%p`qI$%DeL1{>z$iER9dD>J~hI|&L@ zi-)@Q$l2gI?BF`XRPVwEK>mlvMca>GN@#bKaDA-D0N8zm%A*c}cmV#4QH^QD#)V9# zV<$wH<}QipZ1V~772weLh|BYI=bv88o7^w0waY#8iGH18c<<(K%Pk1KyGrZ1Pvt*i zs#-WtF%80+C^tR4clG4o#$3d+HPEjWpPSVo&{6l?H9v#}>1Ic(#`pxc#igc=Gl3hj zi$w`PeG;)Qs@vDv&-#R9&&^U4xkQPso&NeGd1z?o&xa>xI=>w&`?UWTt=dsJTLYF5 zUfv3j3XH?QPI{WL8DJzC@$ZQ0Dg`rsL=TlCrWnXC#1SmUTmkH zbe9v+a6z0S(%gBu=ley657|di;(JVtDLRR!F59GfFE*c}FfbiHqDPvdd(qRRNW2)A z4y-MDgXvHnoXP$J{WW>j7VZaoyqAF_%FE)7tMf@DjhLDuQ7T^+2iA z?AS3^=>74gyw$bvb80p}N~94lOj5k50ixawHb~$GEQ?H*;JwC*E(oi(Bo~%1qUKiy zGy){IejjxDk(4InYN&BZv-yM@AO#{0XGtD~c)15WeX+SF98lmdZ1(MctTgd`g=~XA zID{GxT}U|v(pq{Y`uE>kG&@q|M(Bx@2W3Y$a#?oC$t%<;lx5BWc}a1Q=q*6RNZ6#^ zns#(e?qxMu)1Rzy=Qn|c8SpM`K$9Oz4)^apg&t?baev8q1IyfEtx*I)?k;J< z%|Dor9pU5vGyfiVPHwE2Pl=`>tWHGKC|k@fZS z_hbBR#&>(W%4OeEFxs5tN8v`ZO`{)`j z;7%)dc#uY=ghKcQZL5zcLa@mwUCL-bvHR61A zSXE0-S50h&Q8 zKZmDZ6>0cS8~wy~GI@wE9?aJP*9enIuyLqBp&eZaDl(MXgvYY84}z1VR5u8%L|~KJ z1Xf8eKr-xIfID7I3H-G{QFS>-u?9f~5H3yh-RslWN6lCL&UI_E4XD?f^k(BcPLP{j zA4$z2f%(V#ZDK|g40Vbs*+=A@{+Ke5+m4^`d#buIRq>~zsKd%S>}(~{QBf8=KHx99b2I5!iuq#Km^3)iZ)#dN~8#73Jwl`@>KDVI^4W3jZbZ( zEWejf1)(KR)-^w}U$$mkVL8$*reg4C@IHP0oTUE7;a{rU3aHSM;4ly)RJNH{P$Ioi zWpLlB)~w0|W?mD2Pi>uAY;JwtGD2EUsuKwVg~_|R5|FaruY~T$`>L*`(n%2gS!tK66+X2Ez=cg?Nv7&c~C!CyNU9Q!JiY^G$V!V<}5#2Z+_zYL*? zk0kuaY0I@ETvRZ5T!|Qqgn316cc9H8@tj5afpHhml7EsvYxtoIsoT-OJ zll?!W6+g}KV@E8vtBjclOtf!_)~^inD%>fT2vi+L6(083KqM8tnCCJpQs~n=*5iRy ztMuLiOC^S{Sq~HEA`MgRI7}7knw_FAB>Ifc4o#X) z#ifU1^s0+(U@noKFdbs^l*o=;BjRdd;u^-n^VcA`Y>`5tNdCEi1@viyWF6>jRFXQR z$bHO7@>CexTPTb)Fa&*BFsBM0v)=C~9kJHIl>LX% z$~(#*uKYZB9z;~eSi#~}xL1UTxU7}H7vu}KX1y49J;!t+ETM%v zXMWiAlw3JXmN&}~CGXeic-IRjzkL7i;&jQCMLh1&YLX!8tpP6&yTU>OoZ;GR1s$hs z$s*IWQ@cC|kDjEukR~e7n5)Nh+cOhIN-@#)8ARKUX%YrJ()HOdt1G5F*D`I7po(Pu z;AN3OU33qIbN3OKMQ>3_>Nsy3FQ?c$q#%?KoYNF<+MZjDe*=hD&?l^x%RfW2hY44t z;}?V)erB3X^ntiPcYdNsU-eD0;v&A^gqYr?U|4WO)gM`v^>}*TJm#+0sA&hLkbm8=i=B{b zQV43-D_#BQXYN06Bg;`x$0d#V(`v5F%L^TogqC$~pwQLOnID%y(tp8?CCY*{c$UAn;`-SO;u+?8| ztJMx$;hvzAQHe$5R+oX6@4!Bw{YB=wtw>AS9!Nkwv)&EAHxpQIhXeazf7cCp(|yQY z1wxi{8D6i7Xv1q_90XiKs%KMik#eY2T(r5z`#L(A)Kzi^=s-vLyzgJ01M<=z7UHTt z{+y7<0C^eBO-g5rcJJ9!=zJweoIC#8*@cH+Mg6u)I7#8kB8Z1fF0Kw_E+E2-RwWh_ zjyGb|0V;vG6Cz4f2I#tgHVktg{dDTPHzl~6h zacJ2gt0fhRG3Tj5EI~02ZoLKq&JPpHWU?LM?bq~=$Pw(E=Xc#V{!36ajspLLAIYTz zia|YRmE=G?^SV&w^~6}A?zx!vpVp6hYlnZL-`7&|3c-mFSpt;^QsF0sI3ebBVceA6 zZw!y927)^qU)7#abo+xsy@DZim^Sf`Ee1z-c<<9>;Msxe>$uIwoby~4ovU}|t$540+ zl8wVdkXLV7Vp(|+m8LDDJ`K5b`*z{Of}oOc5xjZ&^bbDps#{+jB%FQy^dY^$u&kx; zuG@o!_$*l>@ti~D zfTD-Eh-os4B6guXx0u(!32jx31n3n&Hq)V%s0E;q4l%9Z2dZmn9?pGFv$4{7kaV^#pvra(jXmL6$aDoO z#*$7gv3$FLo^p7ae_*AvDy+0PT@RQ49*eZ%ZM0a0P|CC>pKQ-JFfba-b~+yI=kK4N za#-gnZPqT$=O(8Jj_1d>Pwx^rfA_%>{dmibZWmShh}~ygS9%eDqoH;BxzQPFr^noH zC?r>-;T z%NF0{WYjs94i%X`9@ne!r9$mf=&O||Kk07zS+gbGblz}T8ovNfpukU@emqs$1T+J2 zJprFtMW!j>ZS#mew(W&4f@(rn=KX};*HLbn_Xs9K7IKVbHnr3?@)8uIF>L6dzucOX z7b;tWhz>bDspPSoerqSe4oIA6imepjOzNxyRAZWny`Ys}i3%DXdxPCxsg)GJ*AQJv zZ%s)&Qrm`DD^UFp$7DBZu{&&LZDmK}>tb-J0(w`(=#^E4zdAtfY% zIjPlnw^daw*7zZpbo+9LW|X_0~+XGBp}Bk7LfCXGQ~Ua%%TLOM?Mno zvN`*|nnoOHGm74o3?o>bq|N1#@(Y@1B*lOtZ)J-T+J2{hZDn%^m+2zjG-eZi%D zrtjHQO-uj`BzH?064)#-Msu3o))@M%ltDtzXHMg+Gf6ak)PNO)OCB{={;izy0$K3& zn4X40E6Gc193~%dZC6=WgC)OW3U07_dtveR(wx~qlI;SvOYdiQL0w>c-Xj0Xg%Jw} zkiv6vlhwW)f*>4slO0E@00QPLcd}9pHFo*|0TXoJ3l%`Kkn{yEIshX2#CAQYIHq>@ ztZHXdlmil6uimUi)!rYo1SD9$`|J{$AjdYKHEqwXO^cAo*o|cg3p8`nc-3{fJLcW? zVGa$WbNMW~dl|u4N9>C7+NNh150Ww=wg)RhRubJ~A4U?lM!LFa2A&5!JJ17-z7cl6 z@UTM+OzEs=697x6LS>Rkn*dEpF*)<%>-KQZgiRz#8n_3bRIZ-!e?6m+v?NI{lXT@! zB@JL@;isbFlX@HRP_(*(HZzvcLL6h9tm9{sZlo|aZr9Ld#pMU_)f?z+7wDQhhZro+ z+#Gss^hgT3JV*@%e~)U%3*c?1^$o*es`Cku8!DuJ&l&)&e_87%8rUBAA50VNHq|6Z zM)*hb15|DU+NQK{2W=W+Wf6tWAb`=mzq$Ge9r*RyaS8sD6*)L<(_+&Trz4&(Uj0br zagb8z=l(SQIn10pE^R4O=&e++Le_2=j0Tatpq1MhA+(^v-^e(69)J*yrL`F)(S>N0 z#}ChlW;4Tv7`l5mc=<((jIJ03v6qf!1v%}!>t6FO^|5_VvR(^mtQ>x3GplPINHVi-zvJtU-lZqznMCS<&bJ%)KBkoi|%mO(zF{7Eed$&OdjAPICjtjJU~d zbU0=?I*dM&mw#QL_J7QEgd|Bm3$ASWEED*0W|XTK4ka_l$)O^(ecT~17(IA>e&c8R z$L&i|EM$-dh<>fNcl=wnWz>EULz1zAUnzz-qvTS2*(bihVaZIoUD~+f8%~~qUErPn z!wyYxSQWxHI?gtVXR^LvFKhAN66s!-f`a~C_jX9DjDvlb?Si-_A>Tl*Q#hroB6+E6N-xLfGG4wDfj= zP^7qug(jxP6>Pr4s>>wlA^G-g%7q?n7~FLpOH!;FTt<*w(&@r9wQnu=F?@I~(}-7W zT1D}-jOdNtd;)1V_~{nIYjoXnt|?5G@4y|h?4Cr^(*`$MB9Tm!#NMB2#8QWat?VMR z=tXLCM7pjG0}V^)zifiA&@z^8x9lt=bqGJ*V!-yMgBVD!86qc5Y0!z>ggD0^9nN}4 zO9<)Sg;1@!X8hD2;C}F+d6h#_m5-zfCfZ9)CD&xd1cymbX9R<@m9@Hc=J|fcQ~1dl ztq{QI7>k1j9%JD0Bb8eU*(;h*8XzLCs*+}g{@*Y_M(V-t{`w1OnT2mtpnu`yq>6x zju~@|Mhi+h%2zwhX`OJ;{0EXDl#J-L9E!nc_-ikF$M2%|6N9EwhH|U-yF?Z*kbv(` zSm>*1iq7_dAn!WU@hYo?c*Ch!S2_%4kWrU`eq-C8B*K3O^$P7MCVUKZw0W!q(1CJ( z+?&Q9cBpodv5GG-jxp^dx89yn5RyQq2e5D`y(Ksb?c2<>HrksO8O^~U${SfSB9U_N zM2yDCLHA^W61M0wDOofXKmLJ)H2_s=2PQ9(Ri5!ftPd-rMJ9~9kZWH@mB9LeB$5y| zVgTjf5;*t?!-UbQQMI!8v-Srok@L@1awRiWL_f%oXEP(O#bFh#yK(EYR2fmKUD%E& z?9<^pTQu`BJ#(Zx6$YkKwF{AApqG5dfigNSsg4UItKK2?DOHa&3J(r}e*tSijSV5H z))~@FBjumHMzrbeOh80Sr6ViSh{X*gmjfT+-gc4nuOJK?DJ`qoQQFBAKDtvIZ~@ot zImEaZ1g0M0L&+kV`W_=|tV3?F_32bo^nM91RHf|kn@)==84jv-WeAZT6?-8E!*QJj zqdtwCc>cl#FL$(4Sh&=Q7Jz@}WaF2GCWVlP;K2w&I3z26RFQcVDe7L0To>XNV2P~Q zVU41sg*ll1STrvlBF=KVRZY*n3EX z|Bvg4gG-#JbdH*w-$aXT z_l$rEsPd~o!{}{hA?xBlW3ttVRx^~&Z-|SJ<>{EXNsFgC)nL)!FClN@QZXj`rGKRmqO@?K(pL!UO!q$LC?oj<%*2$*myd9H{@ zQXmt2HK58jt+Jlgu*g&ZW@7E$G3W^$a!C`_$hhrcT-^h%i#53Bx3YV+n_?;>eQ3SB zQ!4TQ2Wha$0~%8%>`0@-%-V#>h0Ps8l6F&m&qG+>0?+KyJW`=+Un?2@xp5togr;8q zu)^;t2YFwGK{W!^^a#>Z9Z^eqs!#pzHM(kJrn)t4o0TjKoTs%mwwe`o*{+m|S-!qc zRH0k_Zw=;cw)uwLXzn()E??eHH7(jV(1{*HCbExd#4WAi*&&Tw3QI8&2$C?LWpq^c z^XUs#F@xfZKGB6HZDm)E8?guO1ga=OabHqKH$3;M*pyxXrt~~Qk}Px^NrUU2wQoeq z656Z}f0a^RRj$(sugBSRSORm1=xN)67cy{y(ew07x-6RB6o$woJHGLD-JVK+3qY(=PZgAmt<4c_=nNC$e^lgSqc{>cPO%m4DbPx3pNd@KlOL%g|MgSN9K>5DqowdXkLsE zmyF&YqyYrGXq~<_yvlo0Wis+}g3Op|vG+<)9Uw>E@k@kI30Z?m0M?wabEt`Q&B5aV znC{Lh(WM`y;%yli0R2dAr~rj7ALA2UbSYtVtiW(Uy2UjgtmB0JAsnl z5Qw=F7y~K(ivI#~ul2s3qbonbEP(jyGWb|h4e!$kr&kne2m%+=wY;r&`LdqU!cXy1 z3YjfPw9OXS65B&~jKpI)?34q8z(DP4N*?{FmXkQccv*@`FOq}x$EN6PQPH#om*O>4 zeB~3r!v+XdNICqLoOk+WFwrRc>0vA+Zw!3Av|a9#!uevnPn9Mq7E9gfuoXWX0~n3@ z@uS!C6_OyCh4o(^z_}GaH7`=&83sI=I?^{j2&K@78-tNOOP?730tj2e>S~k<7G33u8wJS0b4QagRv%bC&##cP1WB}&d`dKkdkYl zc9Uzl02CpB<)o?}!l7&m*{B&tlXx7#JP#SuDZM!jjuJx=q;>3G$+{M-k3cguxCJE; z7wawt;Y|TD($Tea7E{P33Qq)(KN7_@yjmBd{A%A4e&sdc36Yok4RDw#=M8&O>NY4g z^_lTX4E$qaEq07xJhsHk9(i6EwIxl%?G5ODqjlN9UOr`DkEW@=EY9z$BJq$skteIk?7KJw0r4@U0>=_$^~FJx*BNhrW{Hw5UA(@am|7jH}B z_vc$x9WSL}H|u*Y8uCIdUTg>p+$2$3R%@_cjS{=!WuSn~WYU>z=G~5M zwuNp+lyMDa;ZLGTR$3&gErS(#N9>!Us5?#~%C2}X#wH79qQrjy&g(H%moY@O@+I_( zi2U~1pvBrzhwzY#@O&U${}98ETS<`*;!~_*(pc~m;^_-cF(fg1*|@ZXRH4Djr!Ce_ z3>*8fvJ%7t<#-KjHkmrYi%xsRTNqUQZf64SxqLA{F;;j{NE(E@p}C}njMtr(w2*N` zXf8v1nBF_@y{ysK(W6L}Rg7lu#XzlRMhWmOsL4JPYxVX#xg<`20W_e{avnvPQ_Bym zR49Gg>nAmJJ9MB(b~Y~3Fa@vP(02rky;yu{W2XdIbxriA|BHJXf-##HI9e6ML*h!R zcz7)Y&YM_`CsYar3X+6g6HQrB2P#Ou$@`G$(Etaf^zSLuXT+*T{rqyDUsHVC^;u0j zUy;mHm#0?Jb}UV#APPDbMTV)q2UIPC0F zC43U?1}W-x{G1IQdJpYPmx-0eU_&8nESV{m;HhE}MTJbnP^osidKi5Q&DU_P`D`Sn z7keenCI^LZt4vUkK5EI=;t%(~FrdBnS?sz4TTQ1ixMHG%?zaUsj_p49=FynKQ=xle zzl6nT*6-MGy)pCOU;IxYM2Q#(KaCsP{;3^Lg%WUu>AW?$rXpn@{}=xA0UKc1qSm(4 z>0jFS*}eRi1fre)mBN3e@L!7{NcI1T6nH$B71!s*%5IbY1Bf?t$jHH8L?`F{FW1y1 Ah5!Hn literal 0 HcmV?d00001 diff --git a/examples/shapes/shapes_draw_rectangle_rounded.c b/examples/shapes/shapes_draw_rectangle_rounded.c new file mode 100644 index 00000000..35fe63fb --- /dev/null +++ b/examples/shapes/shapes_draw_rectangle_rounded.c @@ -0,0 +1,87 @@ +/******************************************************************************************* +* +* raylib [shapes] example - draw rectangle rounded (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(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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 + +#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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 HVAB5qG(Qx} 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 00000000..c74cb2a3 --- /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_8XZ1{4ubUVDOp!P>&p#WAEJF8R-|udnOd z0u7{?7AxW)N;nvoT?~-pZM-NTft6saYCVv_o?^1Lx9va%M-LXlpr_2iP52;oNuY)e z+>I9{CtP%~#pMi#?E(^S8+z2X;dH|;h#NNIbOXoaZp#Vyb zqoFVw3Zvx$xX>Og9YOWlXef+^0<3v6nifXW!f0ARZtIWch|wG|nj=PY1bQEPv|Jc1 z7e>p4(Q;w5T);N|Ia)f7mX4#P<7nwPS~`xFj-Zlo(1rq|+k)x;`rY1Ob0Adgf-;NS z^n~7Jo5`Q0yP(VBX05QeGuMR0%{c)Zv4z=a78?#Z6PC7kj-GQ3J)* zy}25j2ccpwgqRi^D=g;384?Y7iUzkBC4F;n&)fp%H2F?sBuQhPwgt`S0_Rkh;hfiP zd?*ff!pbUK&Kz|DC}c)MVKfwArPXL!7)=YKX#u%<9?cP>Ibt+NjOGaR*8FI>Fj_8* zmJ6fh!f3gGZJ1-UbQ~=mM@z@i(s8tO94#F|CE;i&jD~{3;vqj0&p7SkhGn@NG5cyH zk21x^-o2=BT$pL`L(^F{;3VGW#c|-&>Wf9SCTIH~Q(+95iUwA|8J{HVSpb+VUWhTd zoNrjr?;~8KnB$VM#yUg>Vli?^X_@}vR6%iedJki*3Rx*AMI{UlY~#29FPM~gJ>q>NrDca zV`c6 z5_5sGK9+cAeS$Z*2}_4MIh21XS$}bgi4>CwH)Bxxr;bvNb&{+lzhBq}n*?}vhCQ^I zWr_&c0Oy1fwiFXr9J7!a9Kgvrk9mxWhR--iw3x zt*~9R(t4LUi%e>>?cd;8>oQ`K1ijp^MD6xpWBKHk`x<+Wsf(|C(%)r!MK8bo6%VJ5 zOor&W#kq4}(}gb~$&DQmDrhWVa$_#zy7q$O@&~V{iCMV;UFUC@{ zws0~!J+!epY(47}R3}j}#<-uo<${O7#_SGFS-~aWqn<9@W;QG4`y{D)=DFWr%IY_( z{yluCcEd68wU_S)tSv5>TK(doX-t|%=U4HqpA8ol$j#-H%-ssz{76)$>51Q8_t#$P z-WKl0(<$?|nFc*hG1=o*{1{_?m*+hs#XZEE;(+_*j+o)wz6naSLwl-nK{N*gLzDOf W!Qz>Jq=8#$89ZJ6T-G@yGywpnq<5(R 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 00000000..4477203c --- /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 zcmeHOdt8%s7vIL3!=_GsU)+XVCU1B*)PahMz--{B$ff388%Q|7kp^m-d=cRm`VnvtHrl82Byy8SrGo>irde85929?iwHa?J_kM-%Fef06?^E>Bz z&iS76oZtCnsUMH&(plD-!{Kz95IcSnhvOB<;c$-$1@NTTyxpZ7&X_|J#z#$gZtlG1 zAMadUX?Q|Ef*+ATGTUI+3XKLU<3}vw{yh9Al`<<*7?N!e|16d?tS}bkImD7KnZN(x zCMY%LhS-r?Rigu+JEm1|vwgx$@(>yCuAi;2i*JvW>#~?ku*$49hZ)<^n!>c)U2Y+u7 z%0S$iFIlyldiFR*6#FpDonqg@inpBFG~c^T3oj~|CNx^cN+qiKV6|!lXUcVm)y6N4 zR6So{8l}UO`dS`xqPoB|JkYcZQ<~O14K(LU;}-mi*4=;d6N6UHTkkk#qYA0zA?vIE zLw0F$yGx(Zfu?-{rcnzJQ8fih%~s2V7^3gj?({vrI8rr#oGSe^wn*ch`D$gD-xlzN zm3J%A8XMpX-7C1#E&r3)W?+j}gNFo3KS=%Z!0JnG$&{UTI2yA}ivicWB5)YC=!8;JY{{YwcpcNX#ek(QTKW`U6dA4Q zXUQh7nS29ZK?icI>ouR?iwX=@b!t#d#Z| z*gYoPYgqovF?T2Wmb~xu;gKA69Hsmg?0Ue8OVdoF5->_v@Nd3kRVNRpZaux-lw#m`y*9G3$Yu4l6$gN8CFsfb-^KpGDP%|Sdsyd{xnXt z?+os?>b`uzylP60`VPoZdQEQ}@{ugA3F8bP<65y~f$kbX{T~tc5_N47jrtxI)Z>Jr zB1@yQ$1cZ|dn!jimmIrUDE6z8C!Ke*va5$vA@ug872?2S*&{rz$Gkah3Z#3m;>{ z@&!1%0L2)d5{gPirOuyo9lOwCOvdx_<#|;E2h3R&nt!YP93QfRPWF?($89Uh5lhxt znh5Ik_Y(DjtF*R*ShQU|kngNjQQCU3qUJH9QQtvcxPQTyb;-kgXPDQp!@tZ^{KP_s zXGPrQmWr5-n(Vq&cvR8}kGR=;*mnc{Uf|=C%;rGLo;M%l9{y5qy#9;C7S1B((bknGK+)vw*=}NYPw#_N5N!KpqE;rYbswTYz_Yb(;2&>aLfCAR0# zHF{GxuMiEXz`g{vnpN65`m|{*r)hTXo2} z?gJ4x74suWQ?TeYHk)R~i+N^kb?%U(GMkJg#Ectl1S2`94gtaRs(u2*0@YlK|)=#o5{qH?&MTTv-zlD*o&usCRos zHw>(bag)=|3b`qkJJbjA9R_E*$n>uf?e_!WJefojPBuH?^yZbPd`A&ZaQofweu(z_ zfb+ur#3^$=i;!JrMRN6MezBvGBAiRCi#+oC--JVQnr9U3SM)dEJ~Ki#hZX9ez!rPh z2Cy#r+aBahr7b9^w|PZp4?GidVFg|}Ln6Lu;Owtzrs@yYMV@o znut%bRJ~fE$LjpAA%Z?-cwqDV#_Xb8_5>i|l`ptTC4fFH@G-uvJlg*61VB~UP;sWr zbws%7=m;WOE6zWCxpyBFz3L-SalQi;=P|Zo(rR798C=y!5zaC;R{Cp1`+dN9`d}B~ z{F{|<+U_}t%w!_9|Nx{1eRaUZkDsZi=S=$gnu#O!<8k2(oEybx%bk%arN zVXDx6xC_;fifMm8%5w}*ZNhHsXLEl{n-IFU8E~vQq(m0yV8Pwh^#oKlv(^CgNKuE5 z-69XrxSyGi<1UylwC~$Z9Rc)eKklCUfcC|L#EgJat&fb+%Aum{=s$V4(@a!Eaaj;+ zTlwaJ&~L&g)^#swTrJBcBcnBeRdp28uEY^*OEVg34X(@n=U6t{D-TiYZjrcqeH^1! zas8h^!2Tf`a^MxI+J}zYz=`N%LQ&$Z739Ec=e;}>k=R}FpMs<6gd?C@0d**HzWql) z1hAVh_%m#mqnza!@wRv7QG265=~Xqbn5GSm=DAi}oj^PMDcs??RR*gjIh{Cs9Lp(T zTfSG?AV(4{Mwqc$k~EUoZV7KJ~L*6g+HAjfY-*VqaTGH>Gke1y=Qn^xsbnEx0J z2V;=&KERLgILqWAZ&aT!J-SylxnteT0%q4d5xa*{HFi_3h^yIE0Prmg#tUy6F(h zRMB3TDoPnhiF+QGgg#`=tEa^^;)^1?WEP%MQBHf*9c#%?!>HN=Qkx&Xh~u4ESQ$pW z<_NxmCLK8$S%lZfu|+{JotLtSns~e6apKJfFZhG3nT5oZWCW$L8&QjlFT<>`6ina{fu3!n=CK>vKGJ_1_eCk0lGhA*d%&Ge?Vk>S6lup$>hW`r$ znm*~YHd;D31AALPaNDql8pXXE^k-vdn7otBb}8b?fg|AV&iaGOG$Pg>>_xx9dko-| z?q|!waQe+1C*js&2VXJMiIKF}muJ$&9FvEVyFwGO7g5isrnay};8|K`(xDqpD}Qwh-fU{Xo#~<>TA4i62CuVMSi`g>Lzd^6=zsDF{ib-U61|+&u-0J{2C|b`_ zU$%r)VFJXsF(#ck_)lh@br+r(@i+v+l(Ha`cZRvrkKy6xdV;@=uK)BBd~TT6unuz5 z_ScF*{Y#GCV;I$gavZ3iVa_T6vu5A=M7zpw@DgTral%~uteho_Mu2k8E4PaUlS z*zAJ84e0|c1Q30E5AF?U(1#d6^N6+fEQV@QX_(X(AFr+#E`6c$J3Dg_|M+fs$@u5>lcFHZyHns zAk~Ootr?SFhzV}>1Ch2Bz7`H5Df{)!P1=P8NeB>Rw!%E=W1ypTm(DvxVKc8Wh@I01 hFi^ZJ7EF>kD6pHR93vo8#{4D|5nsts@T3=~a4R{aKKv;r!% zd{$U@w{V%fqf%;Z9Ve1MU5?$3EXmMvqGFNfSIb%vx5sOP+IGW_kZ-O5L{g z@Nw6?NkIbm*zie>MjCGf=I?^S6ev@TH_twh*s1ckFqn<=f(5CC2lL;WI9E2 zJ8zV7CcfY9%6;BlU%Ip4v=Ob57JE2|L8b8?EiWchikGqb(r(&C83-;@B?i~T9<1Yx=2}v7BNXEHa!2eqXVRW|^iDa=Dtg4o zI>K&JtI!`5JYW8jz9{PltHBWtV|Iyv;pCFC?{U&0ZSra}_1IIBjqau8Ifg66Ge4m_ zryM*S(bT4d*Egh7ZbRQK{9R|zE=*P=J>8-tpAHjKe!^2JBZg)F2-(6u} z-k$&L-MQ{srhD~OSDWebMsr=O7G@pcy391nTE-q!DAF~+i)3Mj{c5`Hn66Put=M6_=f}zS$Ok zKQnLd#nKQa`-S0vS{LlEGbuZT`%g{ypPEijHo`Z>>sIq6@sG7{{$0&6M)0`>d*?Mt zy^YYpbSonoWwo)PIJq&und|U;B>j%%HXL8st5nZ9B(VYKZXn_I`jfN-02%2jAgGnD z!vDZ35E{KL7+I!<(F1m>r=eRPp=hO~35IhmNy!Hf3xZ9uj)*%a?x=S9%KS3ow)RJq zOBiX+GN<e7~?)&Oq~fGci=#=7@$V4)N#TsofPvvR*>6JDVgiisuQC}O0O?1ih&|AkHHe10eGB&xFR+V$nYOt>If&lk% z-V+_4l=DI$fR=cnL8atq%txG{@ z*%|8MZnd@MItPO1$_LH%Y^JeF9#a(KTk&i`Y|(?cVd$gJh49f6)DHNAaA>YvVXKU8 z0n3nLj-caoria0z?}QUcFZ&|E{Z^_LnlAu!))WjHI=LvneC#_kojJ~QY*9kabyA3X z*}(X!V@2vTCxr$y?(}Gm_ZZwJ$@m`ssmP4TOsbLhxIN7Pz7!-QfP8T9349}=|RR@m%d=2}u z{=Hy_FO{ukMR-5WOTU6`6=Tsv@9m)2nL5 zS50+;UkHG1e5>GM+(&c)|vb>*x0UZ0|41hmR zjty9w_%8`K%)&q|KPE;a@aDRblCMN3AG{|mLAnakz3@NuMhpjwfi|t__W^CNyxgfS z!wifxk@52zETIo}c6ni%;$&h2eX#Sx4R0Zo^(t+8U$omN6qO6Lw*W>y%`?x?oQ->{ zkkAryYH|An5`5m|I#8({jxL4*{UD_a=i^Ii=ow~f07#zi>I95EX-dU?ylmQg#XC$f zsmTD+cFNqTe{}_sxuPagkA4HdJlLqt?vRoY%)>l@MDdq3z76J$v|MtouR>f#>IJuc zfDez}&8d0(df@O-SuUCwV!!bDR1D4^BW6z3=vHen)43h2C+~s5`NU&NgBYAU5IC2u zihb+W0h|L#$v24A28t4!Z?Bpx@Z^D?q(<8}AL6Un&b{A;LPn~p$pk*!dK0ITE`}Z0-Gb?Q9+$B6 zvU6>T>$y>H$^?mWA5YgJflR3YJug$DmL^3~ByvhuLAnZJL0P&N{-5>&KT`fSFL5%F zr1~UR6Iy&ym0*~XqUsb^iP;_Ru3{MUUZHXfxbgi}!b)?UdhN8lc0@Qj^@BIf2XG?N z7GV(JAF_x3uCVGlRmAMN9!4hbc07$)E3TFiEyP%-XXE+U$s97C@!vnDz` zj3zf3&=gaChM6Io8K!4)Lj<*mH}~fBh~Zo$hpXC=?7k;5ZR*?&!jMck1nA811lR#^ zvjY+Q`SNpRV!gTGTf|UPk{>7eYLf%aro@(Fy?H~pQjb_~-h%Yz>jhO}Iv-+211K0+ zP{Lj?-E$lO7*dou6k;*_!~Gf88gKXJg;WCS`)@uw?qfJk&h;wKK4h$Oz|_wml;IY$ z6lna0;nair*Xug)dJsDRnSfpIYa@e(jpf-Sv+Oy(d&(VLw^=3=6@t2#+aMqt(r-ho z^za85{&Y-)@JB1Gz4cmjE+lKFt{A#SWjf!lhov2cDGMpib|U`V^xCI55VFPR z)g7S%T(D$Ji6^{t9WqhWJM!?VoO(#RydDIaG;m2IUJ~12a(2 z4_Qf4(0kQodD`iM^9v=}hX&VWG*%KNjyyFB@`pR!U{TeSe3^zSMkr3&XFnE zaFZ<(Z!#DRPFQDm%`rV(WbT5&dQhUt8*_82EYDbnR}>*hJ&E3;i`UstVhQLW(IvRd zaM5$7Td=$*eQg?olCkdeF95RtgqI=G?7y{V&Hhx`IPsIRwB3#~O3t|NckH1gB~Efp z@X-r72naZ=Uj^XmA3#R}ZXIW-wbKltw5bQ17&Z0$xBOYp)I0|bU_pXl%ZIi7py){y!TqMf{iRvBZ^$ajsf(a6DhM;4QQm_^PYt;HLYzm)=5~qe6prLK`w@MFlo1 z#^4#or{h(DiY9WsANL!5@#5K&W#bs(6lEE+xXRw8I=qX1f4pt|1v;m+1e8vau7Y$G z#N)%?VlObX)6-_zD(Ng~?g~Z`9C_S7C3W~_8zMdXzX?Jigi^h_MxoJ6r;W;Gm6nx>2uYZH5Jvk`cbC6SW*ZDM-mk$J*xS*j4R-6QpjTiZvRn1!3a`uP z=6e|K#UgkFIKp(1-CBt4R@duda9-1ja!qV@xaIdMq{Z4vRc(mN^NW|r2Z_OX5d!B2 zO`X32=NK*I3K}GDlW56B#1lY_e(wEr$9=EBA-_O@u2q=Fg!B;R*YV~+ zGlj&RH$vP%vOCsc+oC~Ut+kM1PCHsT4XT+JY1O3mplb^buElKGCp19)NC5bl_gQ9N znezQ*XKc~N{85W`)23}d6hne4%5pK4#wq}D8adaeLztVe3F3lNVmKF>ox~}!4iBQ!ua;68aImYQ!Eg%o zV@x8I9e5~0yx@V@)t%Fa80rk(nf;Jk+NW~C?LIbj*G-E(#TS{nn#k0B>f3EMJW}aN(wJinrlt^<_G?TMc z*Sn#Zwfb3D&!+*4FzL5IJ}3%UFNCC+I6ds0biI3i%O1CPx+<$ShNoYJaY5mQ;&E7| zq4#%SIo?_Xe+q|6h5k1@y%4ka&vR!?t}y<^uKxB>gQ`2N_EG$E`yn)!*Cet{+rd6Y z5wJ36rVGm-MB{m339Cx3=g1|nN8wuB#liUVykvR#P4hk*n# zXiI!P5#dbgIpk>W*v;g$%2c%)XeFgw0NeBi*rwi3G5m25{H4XL@(?r4Ths@@yz2E! z_V(^t%yqw2{rXssjsIK6dcJD4#?q#~4Jf0isTbXl(LK0{F@^dNK*|iL=~^zvOF?<5 z`CHcP?GRs-v|8h0|CTfRep;R#XKZJ-PBdp!VmN$l&3?UDK4^yMD3D}p_MyzCzk)Rj zCcBd7Xf8U}v&8rhp6l7S7YC+yBS05Ej*5m0kQq$+_2GHQ3fjIT|azPqzoK9lK)h6@aTJ>_ec(RTJ-AR z5mQ+vVt0A6>$xvp{4h?~jLnKx(KU!rt>+j?w99_8ZAnl5AQw-%3er^&o4L}x@cZ@x zca?%T17)iH`!Z187U|~GSoqLsm1A%9MmqFC#ult;=B@<}n4|hlnThDEDn_W-q2}es zR7l6UH|_l9rUENfKg~J?dLugAT8s|28V>mQi7DVl-c~Ra!@JccS3o9d#l|->P!r)m z$Qv1`tJFlH8VV;wc9-*jrM*dJX9p?eU-HK}tB6IRR$#X_wTr<(Y_~%CDAdCG?s}yj zY`41fP!z>>D++Th>)rEIOv*Dt7{Cd4*t^&l6PdON?gpVKHWjR&R<%(C&U_r3`h{-# z=B^W&G!Lu8lBSFB1$P=pRZe@Yl>xjR#AKYuc3CrV%RKEUBcuPOh_0s wBfyB2o}qn}Ly@+w-`Bd(0jodzFf)M=y7nd0mhz<;0lq%8wEf@+({0;-0F21(wg3PC 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 00000000..e99a7e07 --- /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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