1/*******************************************************************************************
2*
3* raygui v2.5 - A simple and easy-to-use immediate-mode-gui library
4*
5* DESCRIPTION:
6*
7* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also possible
8* to be used as a standalone library, as long as input and drawing functions are provided.
9*
10* Controls provided:
11*
12* # Container/separators Controls
13* - WindowBox
14* - GroupBox
15* - Line
16* - Panel
17*
18* # Basic Controls
19* - Label
20* - Button
21* - LabelButton --> Label
22* - ImageButton --> Button
23* - ImageButtonEx --> Button
24* - Toggle
25* - ToggleGroup --> Toggle
26* - CheckBox
27* - ComboBox
28* - DropdownBox
29* - TextBox
30* - TextBoxMulti
31* - ValueBox --> TextBox
32* - Spinner --> Button, ValueBox
33* - Slider
34* - SliderBar --> Slider
35* - ProgressBar
36* - StatusBar
37* - ScrollBar
38* - ScrollPanel
39* - DummyRec
40* - Grid
41*
42* # Advance Controls
43* - ListView --> ListElement
44* - ColorPicker --> ColorPanel, ColorBarHue
45* - MessageBox --> Label, Button
46* - TextInputBox --> Label, TextBox, Button
47*
48* It also provides a set of functions for styling the controls based on its properties (size, color).
49*
50* CONFIGURATION:
51*
52* #define RAYGUI_IMPLEMENTATION
53* Generates the implementation of the library into the included file.
54* If not defined, the library is in header only mode and can be included in other headers
55* or source files without problems. But only ONE file should hold the implementation.
56*
57* #define RAYGUI_STATIC (defined by default)
58* The generated implementation will stay private inside implementation file and all
59* internal symbols and functions will only be visible inside that file.
60*
61* #define RAYGUI_STANDALONE
62* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
63* internally in the library and input management and drawing functions must be provided by
64* the user (check library implementation for further details).
65*
66* #define RAYGUI_RICONS_SUPPORT
67* Includes ricons.h header defining a set of 128 icons (binary format) to be used on
68* multiple controls and following raygui styles
69*
70* #define RAYGUI_TEXTBOX_EXTENDED
71* Enables the advance GuiTextBox()/GuiValueBox()/GuiSpinner() implementation with
72* text selection support and text copy/cut/paste support
73*
74* VERSIONS HISTORY:
75* 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner()
76* 2.3 (29-Apr-2019) Added rIcons auxiliar library and support for it, multiple controls reviewed
77* Refactor all controls drawing mechanism to use control state
78* 2.2 (05-Feb-2019) Added GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls
79* 2.1 (26-Dec-2018) Redesign of GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string
80* Complete redesign of style system (breaking change)
81* 2.0 (08-Nov-2018) Support controls guiLock and custom fonts, reviewed GuiComboBox(), GuiListView()...
82* 1.9 (09-Oct-2018) Controls review: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()...
83* 1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout
84* 1.5 (21-Jun-2017) Working in an improved styles system
85* 1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
86* 1.3 (12-Jun-2017) Redesigned styles system
87* 1.1 (01-Jun-2017) Complete review of the library
88* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
89* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
90* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
91*
92* CONTRIBUTORS:
93* Ramon Santamaria: Supervision, review, redesign, update and maintenance...
94* Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019)
95* Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018)
96* Adria Arranz: Testing and Implementation of additional controls (2018)
97* Jordi Jorba: Testing and Implementation of additional controls (2018)
98* Albert Martos: Review and testing of the library (2015)
99* Ian Eito: Review and testing of the library (2015)
100* Kevin Gato: Initial implementation of basic components (2014)
101* Daniel Nicolas: Initial implementation of basic components (2014)
102*
103*
104* LICENSE: zlib/libpng
105*
106* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
107*
108* This software is provided "as-is", without any express or implied warranty. In no event
109* will the authors be held liable for any damages arising from the use of this software.
110*
111* Permission is granted to anyone to use this software for any purpose, including commercial
112* applications, and to alter it and redistribute it freely, subject to the following restrictions:
113*
114* 1. The origin of this software must not be misrepresented; you must not claim that you
115* wrote the original software. If you use this software in a product, an acknowledgment
116* in the product documentation would be appreciated but is not required.
117*
118* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
119* as being the original software.
120*
121* 3. This notice may not be removed or altered from any source distribution.
122*
123**********************************************************************************************/
124
125#ifndef RAYGUI_H
126#define RAYGUI_H
127
128#define RAYGUI_VERSION "2.5-dev"
129
130#if !defined(RAYGUI_STANDALONE)
131 #include "raylib.h"
132#endif
133
134#if defined(RAYGUI_IMPLEMENTATION)
135 #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
136 #define RAYGUIDEF __declspec(dllexport) extern // We are building raygui as a Win32 shared library (.dll).
137 #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
138 #define RAYGUIDEF __declspec(dllimport) // We are using raygui as a Win32 shared library (.dll)
139 #else
140 #ifdef __cplusplus
141 #define RAYGUIDEF extern "C" // Functions visible from other files (no name mangling of functions in C++)
142 #else
143 #define RAYGUIDEF extern // Functions visible from other files
144 #endif
145 #endif
146#elif defined(RAYGUI_STATIC)
147 #define RAYGUIDEF static // Functions just visible to module including this file
148#endif
149
150#include <stdlib.h> // Required for: atoi()
151
152//----------------------------------------------------------------------------------
153// Defines and Macros
154//----------------------------------------------------------------------------------
155#define TEXTEDIT_CURSOR_BLINK_FRAMES 20 // Text edit controls cursor blink timming
156
157#define NUM_CONTROLS 16 // Number of standard controls
158#define NUM_PROPS_DEFAULT 16 // Number of standard properties
159#define NUM_PROPS_EXTENDED 8 // Number of extended properties
160
161//----------------------------------------------------------------------------------
162// Types and Structures Definition
163// NOTE: Some types are required for RAYGUI_STANDALONE usage
164//----------------------------------------------------------------------------------
165#if defined(RAYGUI_STANDALONE)
166 #ifndef __cplusplus
167 // Boolean type
168 #ifndef true
169 typedef enum { false, true } bool;
170 #endif
171 #endif
172
173 // Vector2 type
174 typedef struct Vector2 {
175 float x;
176 float y;
177 } Vector2;
178
179 // Vector3 type
180 typedef struct Vector3 {
181 float x;
182 float y;
183 float z;
184 } Vector3;
185
186 // Color type, RGBA (32bit)
187 typedef struct Color {
188 unsigned char r;
189 unsigned char g;
190 unsigned char b;
191 unsigned char a;
192 } Color;
193
194 // Rectangle type
195 typedef struct Rectangle {
196 int x;
197 int y;
198 int width;
199 int height;
200 } Rectangle;
201
202 // Texture2D type
203 // NOTE: It should be provided by user
204 typedef struct Texture2D Texture2D;
205
206 // Font type
207 // NOTE: It should be provided by user
208 typedef struct Font Font;
209#endif
210
211#if defined(RAYGUI_TEXTBOX_EXTENDED)
212// Gui text box state data
213typedef struct GuiTextBoxState {
214 int cursor; // Cursor position in text
215 int start; // Text start position (from where we begin drawing the text)
216 int index; // Text start index (index inside the text of `start` always in sync)
217 int select; // Marks position of cursor when selection has started
218} GuiTextBoxState;
219#endif
220
221// Gui control state
222typedef enum {
223 GUI_STATE_NORMAL = 0,
224 GUI_STATE_FOCUSED,
225 GUI_STATE_PRESSED,
226 GUI_STATE_DISABLED,
227} GuiControlState;
228
229// Gui control text alignment
230typedef enum {
231 GUI_TEXT_ALIGN_LEFT = 0,
232 GUI_TEXT_ALIGN_CENTER,
233 GUI_TEXT_ALIGN_RIGHT,
234} GuiTextAlignment;
235
236// Gui controls
237typedef enum {
238 DEFAULT = 0,
239 LABEL, // LABELBUTTON
240 BUTTON, // IMAGEBUTTON
241 TOGGLE, // TOGGLEGROUP
242 SLIDER, // SLIDERBAR
243 PROGRESSBAR,
244 CHECKBOX,
245 COMBOBOX,
246 DROPDOWNBOX,
247 TEXTBOX, // TEXTBOXMULTI
248 VALUEBOX,
249 SPINNER,
250 LISTVIEW,
251 COLORPICKER,
252 SCROLLBAR,
253 RESERVED
254} GuiControl;
255
256// Gui base properties for every control
257typedef enum {
258 BORDER_COLOR_NORMAL = 0,
259 BASE_COLOR_NORMAL,
260 TEXT_COLOR_NORMAL,
261 BORDER_COLOR_FOCUSED,
262 BASE_COLOR_FOCUSED,
263 TEXT_COLOR_FOCUSED,
264 BORDER_COLOR_PRESSED,
265 BASE_COLOR_PRESSED,
266 TEXT_COLOR_PRESSED,
267 BORDER_COLOR_DISABLED,
268 BASE_COLOR_DISABLED,
269 TEXT_COLOR_DISABLED,
270 BORDER_WIDTH,
271 INNER_PADDING,
272 TEXT_ALIGNMENT,
273 RESERVED02
274} GuiControlProperty;
275
276// Gui extended properties depend on control
277// NOTE: We reserve a fixed size of additional properties per control
278
279// DEFAULT properties
280typedef enum {
281 TEXT_SIZE = 16,
282 TEXT_SPACING,
283 LINE_COLOR,
284 BACKGROUND_COLOR,
285} GuiDefaultProperty;
286
287// Label
288//typedef enum { } GuiLabelProperty;
289
290// Button
291//typedef enum { } GuiButtonProperty;
292
293// Toggle / ToggleGroup
294typedef enum {
295 GROUP_PADDING = 16,
296} GuiToggleProperty;
297
298// Slider / SliderBar
299typedef enum {
300 SLIDER_WIDTH = 16,
301 TEXT_PADDING
302} GuiSliderProperty;
303
304// ProgressBar
305//typedef enum { } GuiProgressBarProperty;
306
307// CheckBox
308typedef enum {
309 CHECK_TEXT_PADDING = 16
310} GuiCheckBoxProperty;
311
312// ComboBox
313typedef enum {
314 SELECTOR_WIDTH = 16,
315 SELECTOR_PADDING
316} GuiComboBoxProperty;
317
318// DropdownBox
319typedef enum {
320 ARROW_RIGHT_PADDING = 16,
321} GuiDropdownBoxProperty;
322
323// TextBox / TextBoxMulti / ValueBox / Spinner
324typedef enum {
325 MULTILINE_PADDING = 16,
326 COLOR_SELECTED_FG,
327 COLOR_SELECTED_BG
328} GuiTextBoxProperty;
329
330typedef enum {
331 SELECT_BUTTON_WIDTH = 16,
332 SELECT_BUTTON_PADDING,
333 SELECT_BUTTON_BORDER_WIDTH
334} GuiSpinnerProperty;
335
336// ScrollBar
337typedef enum {
338 ARROWS_SIZE = 16,
339 SLIDER_PADDING,
340 SLIDER_SIZE,
341 SCROLL_SPEED,
342 ARROWS_VISIBLE
343} GuiScrollBarProperty;
344
345// ScrollBar side
346typedef enum {
347 SCROLLBAR_LEFT_SIDE = 0,
348 SCROLLBAR_RIGHT_SIDE
349} GuiScrollBarSide;
350
351// ListView
352typedef enum {
353 ELEMENTS_HEIGHT = 16,
354 ELEMENTS_PADDING,
355 SCROLLBAR_WIDTH,
356 SCROLLBAR_SIDE, // This property defines vertical scrollbar side (SCROLLBAR_LEFT_SIDE or SCROLLBAR_RIGHT_SIDE)
357} GuiListViewProperty;
358
359// ColorPicker
360typedef enum {
361 COLOR_SELECTOR_SIZE = 16,
362 BAR_WIDTH, // Lateral bar width
363 BAR_PADDING, // Lateral bar separation from panel
364 BAR_SELECTOR_HEIGHT, // Lateral bar selector height
365 BAR_SELECTOR_PADDING // Lateral bar selector outer padding
366} GuiColorPickerProperty;
367
368//----------------------------------------------------------------------------------
369// Global Variables Definition
370//----------------------------------------------------------------------------------
371// ...
372
373//----------------------------------------------------------------------------------
374// Module Functions Declaration
375//----------------------------------------------------------------------------------
376
377// Global gui modification functions
378RAYGUIDEF void GuiEnable(void); // Enable gui controls (global state)
379RAYGUIDEF void GuiDisable(void); // Disable gui controls (global state)
380RAYGUIDEF void GuiLock(void); // Lock gui controls (global state)
381RAYGUIDEF void GuiUnlock(void); // Unlock gui controls (global state)
382RAYGUIDEF void GuiState(int state); // Set gui state (global state)
383RAYGUIDEF void GuiFont(Font font); // Set gui custom font (global state)
384RAYGUIDEF void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f
385
386// Style set/get functions
387RAYGUIDEF void GuiSetStyle(int control, int property, int value); // Set one style property
388RAYGUIDEF int GuiGetStyle(int control, int property); // Get one style property
389
390#if defined(RAYGUI_TEXTBOX_EXTENDED)
391// GuiTextBox() extended functions
392RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds); // Sets the active textbox
393RAYGUIDEF Rectangle GuiTextBoxGetActive(void); // Get bounds of active textbox
394RAYGUIDEF void GuiTextBoxSetCursor(int cursor); // Set cursor position of active textbox
395RAYGUIDEF int GuiTextBoxGetCursor(void); // Get cursor position of active textbox
396RAYGUIDEF void GuiTextBoxSetSelection(int start, int length); // Set selection of active textbox
397RAYGUIDEF Vector2 GuiTextBoxGetSelection(void); // Get selection of active textbox (x - selection start y - selection length)
398RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds); // Returns true if a textbox control with specified `bounds` is the active textbox
399RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void); // Get state for the active textbox
400RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state); // Set state for the active textbox (state must be valid else things will break)
401RAYGUIDEF void GuiTextBoxSelectAll(const char *text); // Select all characters in the active textbox (same as pressing `CTRL` + `A`)
402RAYGUIDEF void GuiTextBoxCopy(const char *text); // Copy selected text to clipboard from the active textbox (same as pressing `CTRL` + `C`)
403RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize); // Paste text from clipboard into the textbox (same as pressing `CTRL` + `V`)
404RAYGUIDEF void GuiTextBoxCut(char *text); // Cut selected text in the active textbox and copy it to clipboard (same as pressing `CTRL` + `X`)
405RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before); // Deletes a character or selection before from the active textbox (depending on `before`). Returns bytes deleted.
406RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to); // Get the byte index for a character starting at position `from` with index `start` until position `to`.
407#endif
408
409// Container/separator controls, useful for controls organization
410RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text); // Window Box control, shows a window that can be closed
411RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with title name
412RAYGUIDEF void GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text
413RAYGUIDEF void GuiPanel(Rectangle bounds); // Panel control, useful to group controls
414RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll); // Scroll Panel control
415
416// Basic controls set
417RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text); // Label control, shows text
418RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked
419RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked
420RAYGUIDEF bool GuiImageButton(Rectangle bounds, Texture2D texture); // Image button control, returns true when clicked
421RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle texSource, const char *text); // Image button extended control, returns true when clicked
422RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active); // Toggle Button control, returns true when active
423RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active); // Toggle Group control, returns active toggle index
424RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked); // Check Box control, returns true when active
425RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active); // Combo Box control, returns selected item index
426RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item
427RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value
428RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
429RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text
430RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines
431RAYGUIDEF float GuiSlider(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Slider control, returns selected value
432RAYGUIDEF float GuiSliderBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Slider Bar control, returns selected value
433RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue); // Progress Bar control, shows current progress value
434RAYGUIDEF void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text
435RAYGUIDEF void GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders
436RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll Bar control
437RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs); // Grid control
438
439// Advance controls set
440RAYGUIDEF bool GuiListView(Rectangle bounds, const char *text, int *active, int *scrollIndex, bool editMode); // List View control, returns selected list element index
441RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int *enabled, int *active, int *focus, int *scrollIndex, bool editMode); // List View with extended parameters
442RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const char *message, const char *buttons); // Message Box control, displays a message
443RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *windowTitle, const char *message, char *text, const char *buttons); // Text Input Box control, ask for text
444RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color); // Color Picker control
445
446// Styles loading functions
447RAYGUIDEF void GuiLoadStyle(const char *fileName); // Load style file (.rgs)
448RAYGUIDEF void GuiLoadStyleProps(const int *props, int count); // Load style properties from array
449RAYGUIDEF void GuiLoadStyleDefault(void); // Load style default over global style
450RAYGUIDEF void GuiUpdateStyleComplete(void); // Updates full style properties set with default values
451
452/*
453typedef GuiStyle (unsigned int *)
454RAYGUIDEF GuiStyle LoadGuiStyle(const char *fileName); // Load style from file (.rgs)
455RAYGUIDEF void UnloadGuiStyle(GuiStyle style); // Unload style
456*/
457
458RAYGUIDEF const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended
459
460#endif // RAYGUI_H
461
462
463/***********************************************************************************
464*
465* RAYGUI IMPLEMENTATION
466*
467************************************************************************************/
468
469#if defined(RAYGUI_IMPLEMENTATION)
470
471#if defined(RAYGUI_RICONS_SUPPORT)
472 #if defined(RAYGUI_STANDALONE)
473 #define RICONS_STANDALONE
474 #endif
475
476 #define RICONS_IMPLEMENTATION
477 #include "ricons.h" // Required for: raygui icons
478#endif
479
480#include <stdio.h> // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf()
481#include <string.h> // Required for: strlen() on GuiTextBox()
482
483#if defined(RAYGUI_STANDALONE)
484 #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
485#endif
486
487#ifdef __cplusplus
488 #define RAYGUI_CLITERAL(name) name
489#else
490 #define RAYGUI_CLITERAL(name) (name)
491#endif
492
493//----------------------------------------------------------------------------------
494// Defines and Macros
495//----------------------------------------------------------------------------------
496//...
497
498//----------------------------------------------------------------------------------
499// Types and Structures Definition
500//----------------------------------------------------------------------------------
501// Gui control property style element
502typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
503
504//----------------------------------------------------------------------------------
505// Global Variables Definition
506//----------------------------------------------------------------------------------
507static GuiControlState guiState = GUI_STATE_NORMAL;
508
509static Font guiFont = { 0 }; // NOTE: Highly coupled to raylib
510static bool guiLocked = false;
511static float guiAlpha = 1.0f;
512
513// Global gui style array (allocated on heap by default)
514// NOTE: In raygui we manage a single int array with all the possible style properties.
515// When a new style is loaded, it loads over the global style... but default gui style
516// could always be recovered with GuiLoadStyleDefault()
517static unsigned int guiStyle[NUM_CONTROLS*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED)] = { 0 };
518static bool guiStyleLoaded = false;
519
520#if defined(RAYGUI_TEXTBOX_EXTENDED)
521static Rectangle guiTextBoxActive = { 0 }; // Area of the currently active textbox
522static GuiTextBoxState guiTextBoxState = { .cursor = -1, .start = 0, .index = 0, .select = -1 }; // Keeps state of the active textbox
523#endif
524
525//----------------------------------------------------------------------------------
526// Standalone Mode Functions Declaration
527//
528// NOTE: raygui depend on some raylib input and drawing functions
529// To use raygui as standalone library, below functions must be defined by the user
530//----------------------------------------------------------------------------------
531#if defined(RAYGUI_STANDALONE)
532
533#define KEY_RIGHT 262
534#define KEY_LEFT 263
535#define KEY_DOWN 264
536#define KEY_UP 265
537#define KEY_BACKSPACE 259
538#define KEY_ENTER 257
539#define MOUSE_LEFT_BUTTON 0
540
541// Input required functions
542//-------------------------------------------------------------------------------
543static Vector2 GetMousePosition(void);
544static int GetMouseWheelMove(void);
545static bool IsMouseButtonDown(int button);
546static bool IsMouseButtonPressed(int button);
547static bool IsMouseButtonReleased(int button);
548
549static bool IsKeyDown(int key);
550static bool IsKeyPressed(int key);
551static int GetKeyPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()
552//-------------------------------------------------------------------------------
553
554// Drawing required functions
555//-------------------------------------------------------------------------------
556static void DrawRectangle(int x, int y, int width, int height, Color color);
557static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
558static void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // -- GuiDropdownBox(), GuiScrollBar()
559static void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // -- GuiImageButtonEx()
560//-------------------------------------------------------------------------------
561
562// Text required functions
563//-------------------------------------------------------------------------------
564static Font GetFontDefault(void); // -- GuiLoadStyleDefault()
565static Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // -- GetTextWidth(), GuiTextBoxMulti()
566static void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // -- GuiDrawText()
567
568static Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount); // -- GuiLoadStyle()
569//-------------------------------------------------------------------------------
570
571// raylib functions already implemented in raygui
572//-------------------------------------------------------------------------------
573static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value
574static int ColorToInt(Color color); // Returns hexadecimal value for a Color
575static Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
576static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle
577static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
578
579static void DrawRectangleRec(Rectangle rec, Color color); // Draw rectangle filled with color
580static void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color); // Draw rectangle outlines
581static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient
582//-------------------------------------------------------------------------------
583
584#endif // RAYGUI_STANDALONE
585
586//----------------------------------------------------------------------------------
587// Module specific Functions Declaration
588//----------------------------------------------------------------------------------
589
590// List Element control, returns element state
591static bool GuiListElement(Rectangle bounds, const char *text, bool active, bool editMode);
592
593static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB
594static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV
595
596// Gui get text width using default font
597static int GetTextWidth(const char *text) // TODO: GetTextSize()
598{
599 Vector2 size = { 0 };
600
601 if ((text != NULL) && (text[0] != '\0')) size = MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING));
602
603 // TODO: Consider text icon width here???
604
605 return (int)size.x;
606}
607
608// Get text bounds considering control bounds
609static Rectangle GetTextBounds(int control, Rectangle bounds)
610{
611 Rectangle textBounds = { 0 };
612
613 textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING);
614 textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING);
615 textBounds.width = bounds.width - 2*(GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING));
616 textBounds.height = bounds.height - 2*(GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, INNER_PADDING));
617
618 switch (control)
619 {
620 case COMBOBOX: bounds.width -= (GuiGetStyle(control, SELECTOR_WIDTH) + GuiGetStyle(control, SELECTOR_PADDING)); break;
621 case CHECKBOX: bounds.x += (bounds.width + GuiGetStyle(control, CHECK_TEXT_PADDING)); break;
622 default: break;
623 }
624
625 // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, SPINNER, LISTVIEW (scrollbar?)
626 // More special cases (label side): CHECKBOX, SLIDER
627
628 return textBounds;
629}
630
631// Get text icon if provided and move text cursor
632static const char *GetTextIcon(const char *text, int *iconId)
633{
634#if defined(RAYGUI_RICONS_SUPPORT)
635 if (text[0] == '#') // Maybe we have an icon!
636 {
637 char iconValue[4] = { 0 };
638
639 int i = 1;
640 for (i = 1; i < 4; i++)
641 {
642 if ((text[i] != '#') && (text[i] != '\0')) iconValue[i - 1] = text[i];
643 else break;
644 }
645
646 iconValue[3] = '\0';
647 *iconId = atoi(iconValue);
648
649 // Move text pointer after icon
650 // WARNING: If only icon provided, it could point to EOL character!
651 if (*iconId > 0) text += (i + 1);
652 }
653#endif
654
655 return text;
656}
657
658// Gui draw text using default font
659static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint)
660{
661 #define VALIGN_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect
662
663 if ((text != NULL) && (text[0] != '\0'))
664 {
665 int iconId = 0;
666 text = GetTextIcon(text, &iconId); // Check text for icon and move cursor
667
668 // Get text position depending on alignment and iconId
669 //---------------------------------------------------------------------------------
670 #define ICON_TEXT_PADDING 4
671
672 Vector2 position = { bounds.x, bounds.y };
673
674 // NOTE: We get text size after icon been processed
675 int textWidth = GetTextWidth(text);
676 int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE);
677
678#if defined(RAYGUI_RICONS_SUPPORT)
679 if (iconId > 0)
680 {
681 textWidth += RICONS_SIZE;
682
683 // WARNING: If only icon provided, text could be pointing to eof character!
684 if ((text != NULL) && (text[0] != '\0')) textWidth += ICON_TEXT_PADDING;
685 }
686#endif
687 // Check guiTextAlign global variables
688 switch (alignment)
689 {
690 case GUI_TEXT_ALIGN_LEFT:
691 {
692 position.x = bounds.x;
693 position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height);
694 } break;
695 case GUI_TEXT_ALIGN_CENTER:
696 {
697 position.x = bounds.x + bounds.width/2 - textWidth/2;
698 position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height);
699 } break;
700 case GUI_TEXT_ALIGN_RIGHT:
701 {
702 position.x = bounds.x + bounds.width - textWidth;
703 position.y = bounds.y + bounds.height/2 - textHeight/2 + VALIGN_OFFSET(bounds.height);
704 } break;
705 default: break;
706 }
707 //---------------------------------------------------------------------------------
708
709 // Draw text (with icon if available)
710 //---------------------------------------------------------------------------------
711#if defined(RAYGUI_RICONS_SUPPORT)
712 #define ICON_TEXT_PADDING 4
713
714 if (iconId > 0)
715 {
716 // NOTE: We consider icon height, probably different than text size
717 DrawIcon(iconId, RAYGUI_CLITERAL(Vector2){ position.x, bounds.y + bounds.height/2 - RICONS_SIZE/2 + VALIGN_OFFSET(bounds.height) }, 1, tint);
718 position.x += (RICONS_SIZE + ICON_TEXT_PADDING);
719 }
720#endif
721 DrawTextEx(guiFont, text, position, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), tint);
722 //---------------------------------------------------------------------------------
723 }
724}
725
726// Split controls text into multiple strings
727// Also check for multiple columns (required by GuiToggleGroup())
728static const char **GuiTextSplit(const char *text, int *count, int *textRow);
729
730//----------------------------------------------------------------------------------
731// Module Functions Definition
732//----------------------------------------------------------------------------------
733
734// Enable gui global state
735RAYGUIDEF void GuiEnable(void) { guiState = GUI_STATE_NORMAL; }
736
737// Disable gui global state
738RAYGUIDEF void GuiDisable(void) { guiState = GUI_STATE_DISABLED; }
739
740// Lock gui global state
741RAYGUIDEF void GuiLock(void) { guiLocked = true; }
742
743// Unlock gui global state
744RAYGUIDEF void GuiUnlock(void) { guiLocked = false; }
745
746// Set gui state (global state)
747RAYGUIDEF void GuiState(int state) { guiState = (GuiControlState)state; }
748
749// Define custom gui font
750RAYGUIDEF void GuiFont(Font font)
751{
752 if (font.texture.id > 0)
753 {
754 guiFont = font;
755 GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize);
756 }
757}
758
759// Set gui controls alpha global state
760RAYGUIDEF void GuiFade(float alpha)
761{
762 if (alpha < 0.0f) alpha = 0.0f;
763 else if (alpha > 1.0f) alpha = 1.0f;
764
765 guiAlpha = alpha;
766}
767
768// Set control style property value
769RAYGUIDEF void GuiSetStyle(int control, int property, int value)
770{
771 if (!guiStyleLoaded) GuiLoadStyleDefault();
772 guiStyle[control*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property] = value;
773}
774
775// Get control style property value
776RAYGUIDEF int GuiGetStyle(int control, int property)
777{
778 if (!guiStyleLoaded) GuiLoadStyleDefault();
779 return guiStyle[control*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property];
780}
781
782#if defined(RAYGUI_TEXTBOX_EXTENDED)
783// Sets the active textbox (reseting state of the previous active textbox)
784RAYGUIDEF void GuiTextBoxSetActive(Rectangle bounds)
785{
786 guiTextBoxActive = bounds;
787 guiTextBoxState = (GuiTextBoxState){ .cursor = -1, .start = 0, .index = 0, .select = -1 };
788}
789
790// Gets bounds of active textbox
791RAYGUIDEF Rectangle GuiTextBoxGetActive(void) { return guiTextBoxActive; }
792
793// Set cursor position of active textbox
794RAYGUIDEF void GuiTextBoxSetCursor(int cursor)
795{
796 guiTextBoxState.cursor = (cursor < 0) ? -1 : cursor;
797 guiTextBoxState.start = -1; // Mark this to be recalculated
798}
799
800// Get cursor position of active textbox
801RAYGUIDEF int GuiTextBoxGetCursor(void) { return guiTextBoxState.cursor; }
802
803// Set selection of active textbox
804RAYGUIDEF void GuiTextBoxSetSelection(int start, int length)
805{
806 if(start < 0) start = 0;
807 if(length < 0) length = 0;
808 GuiTextBoxSetCursor(start + length);
809 guiTextBoxState.select = start;
810}
811
812// Get selection of active textbox
813RAYGUIDEF Vector2 GuiTextBoxGetSelection(void)
814{
815 if(guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor)
816 return RAYGUI_CLITERAL(Vector2){ 0 };
817 else if(guiTextBoxState.cursor > guiTextBoxState.select)
818 return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.select, guiTextBoxState.cursor - guiTextBoxState.select };
819
820 return RAYGUI_CLITERAL(Vector2){ guiTextBoxState.cursor, guiTextBoxState.select - guiTextBoxState.cursor };
821}
822
823// Returns true if a textbox control with specified `bounds` is the active textbox
824RAYGUIDEF bool GuiTextBoxIsActive(Rectangle bounds)
825{
826 return (bounds.x == guiTextBoxActive.x && bounds.y == guiTextBoxActive.y &&
827 bounds.width == guiTextBoxActive.width && bounds.height == guiTextBoxActive.height);
828}
829RAYGUIDEF GuiTextBoxState GuiTextBoxGetState(void) { return guiTextBoxState; }
830RAYGUIDEF void GuiTextBoxSetState(GuiTextBoxState state)
831{
832 // NOTE: should we check if state values are valid ?!?
833 guiTextBoxState = state;
834}
835#endif
836
837// Window Box control
838RAYGUIDEF bool GuiWindowBox(Rectangle bounds, const char *text)
839{
840 #define WINDOW_CLOSE_BUTTON_PADDING 2
841 #define WINDOW_STATUSBAR_HEIGHT 24
842
843 GuiControlState state = guiState;
844 bool clicked = false;
845
846 Rectangle statusBar = { bounds.x, bounds.y, bounds.width, WINDOW_STATUSBAR_HEIGHT };
847 if (bounds.height < WINDOW_STATUSBAR_HEIGHT*2) bounds.height = WINDOW_STATUSBAR_HEIGHT*2;
848
849 Rectangle buttonRec = { statusBar.x + statusBar.width - GuiGetStyle(DEFAULT, BORDER_WIDTH) - WINDOW_CLOSE_BUTTON_PADDING - 20,
850 statusBar.y + GuiGetStyle(DEFAULT, BORDER_WIDTH) + WINDOW_CLOSE_BUTTON_PADDING, 18, 18 };
851 // Update control
852 //--------------------------------------------------------------------
853 // NOTE: Logic is directly managed by button
854 //--------------------------------------------------------------------
855
856 // Draw control
857 //--------------------------------------------------------------------
858
859 // Draw window base
860 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, BORDER + (state*3))), guiAlpha));
861 DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH),
862 bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 },
863 Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha));
864
865 // Draw window header as status bar
866 int defaultPadding = GuiGetStyle(DEFAULT, INNER_PADDING);
867 int defaultTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
868 GuiSetStyle(DEFAULT, INNER_PADDING, 8);
869 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
870 GuiStatusBar(statusBar, text);
871 GuiSetStyle(DEFAULT, INNER_PADDING, defaultPadding);
872 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, defaultTextAlign);
873
874 // Draw window close button
875 int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
876 int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
877 GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
878 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
879#if defined(RAYGUI_RICONS_SUPPORT)
880 clicked = GuiButton(buttonRec, GuiIconText(RICON_CROSS_SMALL, NULL));
881#else
882 clicked = GuiButton(buttonRec, "x");
883#endif
884 GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
885 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
886 //--------------------------------------------------------------------
887
888 return clicked;
889}
890
891// Group Box control with title name
892RAYGUIDEF void GuiGroupBox(Rectangle bounds, const char *text)
893{
894 #define GROUPBOX_LINE_THICK 1
895 #define GROUPBOX_TEXT_PADDING 10
896 #define GROUPBOX_PADDING 2
897
898 GuiControlState state = guiState;
899
900 // Draw control
901 //--------------------------------------------------------------------
902 DrawRectangle(bounds.x, bounds.y, GROUPBOX_LINE_THICK, bounds.height, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha));
903 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));
904 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));
905
906 GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, 1 }, text);
907 //--------------------------------------------------------------------
908}
909
910// Line control
911RAYGUIDEF void GuiLine(Rectangle bounds, const char *text)
912{
913 #define LINE_THICK 1
914 #define LINE_TEXT_PADDING 10
915 #define LINE_TEXT_SPACING 2
916
917 GuiControlState state = guiState;
918
919 Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha);
920
921 // Draw control
922 //--------------------------------------------------------------------
923 if (text == NULL) DrawRectangle(bounds.x, bounds.y + bounds.height/2, bounds.width, 1, color);
924 else
925 {
926 Rectangle textBounds = { 0 };
927 textBounds.width = GetTextWidth(text) + 2*LINE_TEXT_SPACING; // TODO: Consider text icon
928 textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE);
929 textBounds.x = bounds.x + LINE_TEXT_PADDING + LINE_TEXT_SPACING;
930 textBounds.y = bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
931
932 // Draw line with embedded text label: "--- text --------------"
933 DrawRectangle(bounds.x, bounds.y, LINE_TEXT_PADDING, 1, color);
934 GuiLabel(textBounds, text);
935 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);
936 }
937 //--------------------------------------------------------------------
938}
939
940// Panel control
941RAYGUIDEF void GuiPanel(Rectangle bounds)
942{
943 #define PANEL_BORDER_WIDTH 1
944
945 GuiControlState state = guiState;
946
947 // Draw control
948 //--------------------------------------------------------------------
949 DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha));
950 DrawRectangleLinesEx(bounds, PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha));
951 //--------------------------------------------------------------------
952}
953
954// Scroll Panel control
955RAYGUIDEF Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, Vector2 *scroll)
956{
957 GuiControlState state = guiState;
958
959 Vector2 scrollPos = { 0.0f, 0.0f };
960 if (scroll != NULL) scrollPos = *scroll;
961
962 bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
963 bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
964
965 // Recheck to account for the other scrollbar being visible
966 if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
967 if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
968
969 const int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
970 const int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
971 const Rectangle horizontalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)horizontalScrollBarWidth };
972 const Rectangle verticalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)verticalScrollBarWidth, (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) };
973
974 // Calculate view area (area without the scrollbars)
975 Rectangle view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)?
976 RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } :
977 RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth };
978
979 // Clip view area to the actual content size
980 if (view.width > content.width) view.width = content.width;
981 if (view.height > content.height) view.height = content.height;
982
983 // TODO: Review!
984 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);
985 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);
986 const int verticalMin = hasVerticalScrollBar? -GuiGetStyle(DEFAULT, BORDER_WIDTH) : -GuiGetStyle(DEFAULT, BORDER_WIDTH);
987 const int verticalMax = hasVerticalScrollBar? content.height - bounds.height + horizontalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) : -GuiGetStyle(DEFAULT, BORDER_WIDTH);
988
989 // Update control
990 //--------------------------------------------------------------------
991 if ((state != GUI_STATE_DISABLED) && !guiLocked)
992 {
993 Vector2 mousePoint = GetMousePosition();
994
995 // Check button state
996 if (CheckCollisionPointRec(mousePoint, bounds))
997 {
998 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
999 else state = GUI_STATE_FOCUSED;
1000
1001 if (hasHorizontalScrollBar)
1002 {
1003 if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
1004 if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
1005 }
1006
1007 if (hasVerticalScrollBar)
1008 {
1009 if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
1010 if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
1011 }
1012
1013 scrollPos.y += GetMouseWheelMove()*20;
1014 }
1015 }
1016
1017 // Normalize scroll values
1018 if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin;
1019 if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax;
1020 if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin;
1021 if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax;
1022 //--------------------------------------------------------------------
1023
1024 // Draw control
1025 //--------------------------------------------------------------------
1026 DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background
1027
1028 // Save size of the scrollbar slider
1029 const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE);
1030
1031 // Draw horizontal scrollbar if visible
1032 if (hasHorizontalScrollBar)
1033 {
1034 // Change scrollbar slider size to show the diff in size between the content width and the widget width
1035 GuiSetStyle(SCROLLBAR, SLIDER_SIZE, ((bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/content.width)*(bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth));
1036 scrollPos.x = -GuiScrollBar(horizontalScrollBar, -scrollPos.x, horizontalMin, horizontalMax);
1037 }
1038
1039 // Draw vertical scrollbar if visible
1040 if (hasVerticalScrollBar)
1041 {
1042 // Change scrollbar slider size to show the diff in size between the content height and the widget height
1043 GuiSetStyle(SCROLLBAR, SLIDER_SIZE, ((bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/content.height)* (bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth));
1044 scrollPos.y = -GuiScrollBar(verticalScrollBar, -scrollPos.y, verticalMin, verticalMax);
1045 }
1046
1047 // Draw detail corner rectangle if both scroll bars are visible
1048 if (hasHorizontalScrollBar && hasVerticalScrollBar)
1049 {
1050 // TODO: Consider scroll bars side
1051 DrawRectangle(horizontalScrollBar.x + horizontalScrollBar.width + 2,
1052 verticalScrollBar.y + verticalScrollBar.height + 2,
1053 horizontalScrollBarWidth - 4, verticalScrollBarWidth - 4,
1054 Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state * 3))), guiAlpha));
1055 }
1056
1057 // Set scrollbar slider size back to the way it was before
1058 GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider);
1059
1060 // Draw scrollbar lines depending on current state
1061 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, (float)BORDER + (state*3))), guiAlpha));
1062 //--------------------------------------------------------------------
1063
1064 if (scroll != NULL) *scroll = scrollPos;
1065
1066 return view;
1067}
1068
1069// Label control
1070RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text)
1071{
1072 GuiControlState state = guiState;
1073
1074 // Update control
1075 //--------------------------------------------------------------------
1076 // ...
1077 //--------------------------------------------------------------------
1078
1079 // Draw control
1080 //--------------------------------------------------------------------
1081 GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, (state == GUI_STATE_DISABLED)? TEXT_COLOR_DISABLED : TEXT_COLOR_NORMAL)), guiAlpha));
1082 //--------------------------------------------------------------------
1083}
1084
1085// Button control, returns true when clicked
1086RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text)
1087{
1088 GuiControlState state = guiState;
1089 bool pressed = false;
1090
1091 // Update control
1092 //--------------------------------------------------------------------
1093 if ((state != GUI_STATE_DISABLED) && !guiLocked)
1094 {
1095 Vector2 mousePoint = GetMousePosition();
1096
1097 // Check button state
1098 if (CheckCollisionPointRec(mousePoint, bounds))
1099 {
1100 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
1101 else state = GUI_STATE_FOCUSED;
1102
1103 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
1104 }
1105 }
1106 //--------------------------------------------------------------------
1107
1108 // Draw control
1109 //--------------------------------------------------------------------
1110 DrawRectangleLinesEx(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha));
1111 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));
1112
1113 GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha));
1114 //------------------------------------------------------------------
1115
1116 return pressed;
1117}
1118
1119// Label button control
1120RAYGUIDEF bool GuiLabelButton(Rectangle bounds, const char *text)
1121{
1122 GuiControlState state = guiState;
1123 bool pressed = false;
1124
1125 // Update control
1126 //--------------------------------------------------------------------
1127 if ((state != GUI_STATE_DISABLED) && !guiLocked)
1128 {
1129 Vector2 mousePoint = GetMousePosition();
1130
1131 // Check checkbox state
1132 if (CheckCollisionPointRec(mousePoint, bounds))
1133 {
1134 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
1135 else state = GUI_STATE_FOCUSED;
1136
1137 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
1138 }
1139 }
1140 //--------------------------------------------------------------------
1141
1142 // Draw control
1143 //--------------------------------------------------------------------
1144 GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
1145 //--------------------------------------------------------------------
1146
1147 return pressed;
1148}
1149
1150// Image button control, returns true when clicked
1151RAYGUIDEF bool GuiImageButton(Rectangle bounds, Texture2D texture)
1152{
1153 return GuiImageButtonEx(bounds, texture, RAYGUI_CLITERAL(Rectangle){ 0, 0, (float)texture.width, (float)texture.height }, NULL);
1154}
1155
1156// Image button control, returns true when clicked
1157RAYGUIDEF bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle texSource, const char *text)
1158{
1159 GuiControlState state = guiState;
1160 bool clicked = false;
1161
1162 // Update control
1163 //--------------------------------------------------------------------
1164 if ((state != GUI_STATE_DISABLED) && !guiLocked)
1165 {
1166 Vector2 mousePoint = GetMousePosition();
1167
1168 // Check button state
1169 if (CheckCollisionPointRec(mousePoint, bounds))
1170 {
1171 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
1172 else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) clicked = true;
1173 else state = GUI_STATE_FOCUSED;
1174 }
1175 }
1176 //--------------------------------------------------------------------
1177
1178 // Draw control
1179 //--------------------------------------------------------------------
1180 DrawRectangleLinesEx(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha));
1181 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));
1182
1183 if (text != NULL) GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha));
1184 if (texture.id > 0) DrawTextureRec(texture, texSource, RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width/2 - (texSource.width + GuiGetStyle(BUTTON, INNER_PADDING)/2)/2, bounds.y + bounds.height/2 - texSource.height/2 }, Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha));
1185 //------------------------------------------------------------------
1186
1187 return clicked;
1188}
1189
1190// Toggle Button control, returns true when active
1191RAYGUIDEF bool GuiToggle(Rectangle bounds, const char *text, bool active)
1192{
1193 GuiControlState state = guiState;
1194
1195 // Update control
1196 //--------------------------------------------------------------------
1197 if ((state != GUI_STATE_DISABLED) && !guiLocked)
1198 {
1199 Vector2 mousePoint = GetMousePosition();
1200
1201 // Check toggle button state
1202 if (CheckCollisionPointRec(mousePoint, bounds))
1203 {
1204 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
1205 else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
1206 {
1207 state = GUI_STATE_NORMAL;
1208 active = !active;
1209 }
1210 else state = GUI_STATE_FOCUSED;
1211 }
1212 }
1213 //--------------------------------------------------------------------
1214
1215 // Draw control
1216 //--------------------------------------------------------------------
1217 if (state == GUI_STATE_NORMAL)
1218 {
1219 DrawRectangleLinesEx(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha));
1220 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));
1221
1222 GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, (active? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha));
1223 }
1224 else
1225 {
1226 DrawRectangleLinesEx(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha));
1227 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));
1228
1229 GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha));
1230 }
1231 //--------------------------------------------------------------------
1232
1233 return active;
1234}
1235
1236// Toggle Group control, returns toggled button index
1237RAYGUIDEF int GuiToggleGroup(Rectangle bounds, const char *text, int active)
1238{
1239 float initBoundsX = bounds.x;
1240
1241 // Get substrings elements from text (elements pointers)
1242 int rows[64] = { 0 };
1243 int elementsCount = 0;
1244 const char **elementsPtrs = GuiTextSplit(text, &elementsCount, rows);
1245
1246 int prevRow = rows[0];
1247
1248 for (int i = 0; i < elementsCount; i++)
1249 {
1250 if (prevRow != rows[i])
1251 {
1252 bounds.x = initBoundsX;
1253 bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING));
1254 prevRow = rows[i];
1255 }
1256
1257 if (i == active) GuiToggle(bounds, elementsPtrs[i], true);
1258 else if (GuiToggle(bounds, elementsPtrs[i], false) == true) active = i;
1259
1260 bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING));
1261 }
1262
1263 return active;
1264}
1265
1266// Check Box control, returns true when active
1267RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked)
1268{
1269 GuiControlState state = guiState;
1270
1271 Rectangle textBounds = { 0 };
1272 textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING);
1273 textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
1274 textBounds.width = GetTextWidth(text); // TODO: Consider text icon
1275 textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE);
1276
1277 // Update control
1278 //--------------------------------------------------------------------
1279 if ((state != GUI_STATE_DISABLED) && !guiLocked)
1280 {
1281 Vector2 mousePoint = GetMousePosition();
1282
1283 // Check checkbox state
1284 if (CheckCollisionPointRec(mousePoint, RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING), bounds.height }))
1285 {
1286 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
1287 else state = GUI_STATE_FOCUSED;
1288
1289 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) checked = !checked;
1290 }
1291 }
1292 //--------------------------------------------------------------------
1293
1294 // Draw control
1295 //--------------------------------------------------------------------
1296 DrawRectangleLinesEx(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha));
1297 if (checked) DrawRectangle(bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING),
1298 bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING),
1299 bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING)),
1300 bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, INNER_PADDING)),
1301 Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)), guiAlpha));
1302
1303 // NOTE: Forced left text alignment
1304 GuiDrawText(text, textBounds, GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
1305 //--------------------------------------------------------------------
1306
1307 return checked;
1308}
1309
1310// Combo Box control, returns selected item index
1311RAYGUIDEF int GuiComboBox(Rectangle bounds, const char *text, int active)
1312{
1313 GuiControlState state = guiState;
1314
1315 bounds.width -= (GuiGetStyle(COMBOBOX, SELECTOR_WIDTH) + GuiGetStyle(COMBOBOX, SELECTOR_PADDING));
1316
1317 Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, SELECTOR_PADDING),
1318 (float)bounds.y, (float)GuiGetStyle(COMBOBOX, SELECTOR_WIDTH), (float)bounds.height };
1319
1320 // Get substrings elements from text (elements pointers, lengths and count)
1321 int elementsCount = 0;
1322 const char **elementsPtrs = GuiTextSplit(text, &elementsCount, NULL);
1323
1324 if (active < 0) active = 0;
1325 else if (active > elementsCount - 1) active = elementsCount - 1;
1326
1327 // Update control
1328 //--------------------------------------------------------------------
1329 if ((state != GUI_STATE_DISABLED) && !guiLocked)
1330 {
1331 Vector2 mousePoint = GetMousePosition();
1332
1333 if (CheckCollisionPointRec(mousePoint, bounds) ||
1334 CheckCollisionPointRec(mousePoint, selector))
1335 {
1336 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
1337 {
1338 active += 1;
1339 if (active >= elementsCount) active = 0;
1340 }
1341
1342 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
1343 else state = GUI_STATE_FOCUSED;
1344 }
1345 }
1346 //--------------------------------------------------------------------
1347
1348 // Draw control
1349 //--------------------------------------------------------------------
1350 // Draw combo box main
1351 DrawRectangleLinesEx(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha));
1352 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));
1353
1354 GuiDrawText(elementsPtrs[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha));
1355
1356 // Draw selector using a custom button
1357 // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
1358 int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
1359 int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
1360 GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
1361 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
1362
1363 GuiButton(selector, TextFormat("%i/%i", active + 1, elementsCount));
1364
1365 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
1366 GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
1367 //--------------------------------------------------------------------
1368
1369 return active;
1370}
1371
1372// Dropdown Box control, returns selected item
1373RAYGUIDEF bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode)
1374{
1375 GuiControlState state = guiState;
1376
1377 // Get substrings elements from text (elements pointers, lengths and count)
1378 int elementsCount = 0;
1379 const char **elementsPtrs = GuiTextSplit(text, &elementsCount, NULL);
1380
1381 bool pressed = false;
1382 int auxActive = *active;
1383
1384 Rectangle closeBounds = bounds;
1385 Rectangle openBounds = bounds;
1386
1387 openBounds.height *= (elementsCount + 1);
1388
1389 // Update control
1390 //--------------------------------------------------------------------
1391 if (guiLocked && editMode) guiLocked = false;
1392
1393 if ((state != GUI_STATE_DISABLED) && !guiLocked)
1394 {
1395 Vector2 mousePoint = GetMousePosition();
1396
1397 if (editMode) state = GUI_STATE_PRESSED;
1398
1399 if (!editMode)
1400 {
1401 if (CheckCollisionPointRec(mousePoint, closeBounds))
1402 {
1403 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
1404
1405 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
1406 else state = GUI_STATE_FOCUSED;
1407 }
1408 }
1409 else
1410 {
1411 if (CheckCollisionPointRec(mousePoint, closeBounds))
1412 {
1413 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
1414 }
1415 else if (!CheckCollisionPointRec(mousePoint, openBounds))
1416 {
1417 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
1418 }
1419 }
1420 }
1421 //--------------------------------------------------------------------
1422
1423 // Draw control
1424 //--------------------------------------------------------------------
1425
1426 // TODO: Review this ugly hack... DROPDOWNBOX depends on GuiListElement() that uses DEFAULT_TEXT_ALIGNMENT
1427 int tempTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
1428 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT));
1429
1430 switch (state)
1431 {
1432 case GUI_STATE_NORMAL:
1433 {
1434 DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_NORMAL)), guiAlpha));
1435 DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_NORMAL)), guiAlpha));
1436
1437 GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false);
1438 } break;
1439 case GUI_STATE_FOCUSED:
1440 {
1441 GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, editMode);
1442 } break;
1443 case GUI_STATE_PRESSED:
1444 {
1445 if (!editMode) GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true);
1446 if (editMode)
1447 {
1448 GuiPanel(openBounds);
1449
1450 GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], true, true);
1451
1452 for (int i = 0; i < elementsCount; i++)
1453 {
1454 if (i == auxActive && editMode)
1455 {
1456 if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height*(i + 1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING),
1457 bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) },
1458 elementsPtrs[i], true, true) == false) pressed = true;
1459 }
1460 else
1461 {
1462 if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height*(i+1) + GuiGetStyle(DROPDOWNBOX, INNER_PADDING),
1463 bounds.width, bounds.height - GuiGetStyle(DROPDOWNBOX, INNER_PADDING) },
1464 elementsPtrs[i], false, true))
1465 {
1466 auxActive = i;
1467 pressed = true;
1468 }
1469 }
1470 }
1471 }
1472 } break;
1473 case GUI_STATE_DISABLED:
1474 {
1475 DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_DISABLED)), guiAlpha));
1476 DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_DISABLED)), guiAlpha));
1477
1478 GuiListElement(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, bounds.width, bounds.height }, elementsPtrs[auxActive], false, false);
1479 } break;
1480 default: break;
1481 }
1482
1483 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, tempTextAlign);
1484
1485 // TODO: Avoid this function, use icon instead or 'v'
1486 DrawTriangle(RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2 },
1487 RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 5, bounds.y + bounds.height/2 - 2 + 5 },
1488 RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING) + 10, bounds.y + bounds.height/2 - 2 },
1489 Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha));
1490
1491 //GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
1492 // GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha));
1493 //--------------------------------------------------------------------
1494
1495 *active = auxActive;
1496 return pressed;
1497}
1498
1499#if defined(RAYGUI_TEXTBOX_EXTENDED)
1500// Spinner control, returns selected value
1501// NOTE: Requires static variables: timer, valueSpeed - ERROR!
1502RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode)
1503{
1504 #define GUI_SPINNER_HOLD_SPEED 0.2f // Min 200ms delay
1505
1506 static float timer = 0.0f;
1507
1508 int tempValue = *value;
1509 const float time = GetTime(); // Get current time
1510 bool pressed = false, active = GuiTextBoxIsActive(bounds);
1511
1512 Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING), bounds.y,
1513 bounds.width - 2*(GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING)), bounds.height };
1514 Rectangle leftButtonBound = { bounds.x, bounds.y, GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.height };
1515 Rectangle rightButtonBound = { bounds.x + bounds.width - GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.y, GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), bounds.height };
1516
1517 // Update control
1518 //--------------------------------------------------------------------
1519 Vector2 mouse = GetMousePosition();
1520 if (tempValue < minValue) tempValue = minValue;
1521 if (tempValue > maxValue) tempValue = maxValue;
1522
1523 if (editMode)
1524 {
1525 if (!active)
1526 {
1527 // This becomes the active textbox when mouse is pressed or held inside bounds
1528 if ((IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonDown(MOUSE_LEFT_BUTTON)) &&
1529 CheckCollisionPointRec(mouse, bounds))
1530 {
1531 GuiTextBoxSetActive(bounds);
1532 active = true;
1533 }
1534 }
1535 }
1536
1537 // Reset timer when one of the buttons is clicked (without this, holding the button down will not behave correctly)
1538 if ((CheckCollisionPointRec(mouse, leftButtonBound) || CheckCollisionPointRec(mouse, rightButtonBound)) &&
1539 IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
1540 {
1541 timer = time;
1542 }
1543 //--------------------------------------------------------------------
1544
1545 // Draw control
1546 //--------------------------------------------------------------------
1547 if (GuiTextBoxIsActive(bounds)) guiTextBoxActive = spinner; // Set our spinner as the active textbox
1548 pressed = GuiValueBox(spinner, &tempValue, minValue, maxValue, editMode);
1549 if (GuiTextBoxIsActive(spinner)) guiTextBoxActive = bounds; // Revert change
1550
1551 // Draw value selector custom buttons
1552 // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
1553 int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
1554 GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
1555
1556 int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
1557 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
1558
1559 char *icon = "<";
1560#if defined(RAYGUI_RICONS_SUPPORT)
1561 icon = (char *)GuiIconText(RICON_ARROW_LEFT_FILL, NULL);
1562#endif
1563 if (GuiButton(leftButtonBound, icon) || // NOTE: also decrease value when the button is held down
1564 (IsMouseButtonDown(MOUSE_LEFT_BUTTON) &&
1565 CheckCollisionPointRec(mouse, leftButtonBound) &&
1566 (time - timer) > GUI_SPINNER_HOLD_SPEED))
1567 {
1568 tempValue--;
1569 }
1570
1571 icon = ">";
1572#if defined(RAYGUI_RICONS_SUPPORT)
1573 icon = (char *)GuiIconText(RICON_ARROW_RIGHT_FILL, NULL);
1574#endif
1575 if (GuiButton(rightButtonBound, icon) || // NOTE: also increase value when the button is held down
1576 (IsMouseButtonDown(MOUSE_LEFT_BUTTON) &&
1577 CheckCollisionPointRec(mouse, rightButtonBound) &&
1578 (time - timer) > GUI_SPINNER_HOLD_SPEED))
1579 {
1580 tempValue++;
1581 }
1582
1583 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
1584 GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
1585 //--------------------------------------------------------------------
1586
1587 if (tempValue < minValue) tempValue = minValue;
1588 if (tempValue > maxValue) tempValue = maxValue;
1589
1590 // Reset timer
1591 if (active && (((time - timer) > GUI_SPINNER_HOLD_SPEED) || (timer == 0.0f) || (timer > time))) timer = time;
1592
1593 *value = tempValue;
1594
1595 return pressed;
1596}
1597
1598// Value Box control, updates input text with numbers
1599RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode)
1600{
1601 #define VALUEBOX_MAX_CHARS 32
1602
1603 char text[VALUEBOX_MAX_CHARS + 1] = { 0 };
1604 sprintf(text, "%i", *value);
1605
1606 bool pressed = GuiTextBox(bounds, text, VALUEBOX_MAX_CHARS, editMode);
1607 *value = atoi(text);
1608
1609 if (*value > maxValue) *value = maxValue;
1610 else if (*value < minValue) *value = minValue;
1611
1612 return pressed;
1613}
1614
1615enum {
1616 GUI_MEASURE_MODE_CURSOR_END = 0xA,
1617 GUI_MEASURE_MODE_CURSOR_POS,
1618 GUI_MEASURE_MODE_CURSOR_COORDS,
1619};
1620
1621// Required by GuiTextBox()
1622// Highly synchronized with calculations in DrawTextRecEx()
1623static int GuiMeasureTextBox(const char *text, int length, Rectangle rec, int *pos, int mode)
1624{
1625 // Get gui font properties
1626 const Font font = guiFont;
1627 const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE);
1628 const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING);
1629
1630 int textOffsetX = 0; // Offset between characters
1631 float scaleFactor = 0.0f;
1632
1633 int letter = 0; // Current character
1634 int index = 0; // Index position in sprite font
1635
1636 scaleFactor = fontSize/font.baseSize;
1637
1638 int i = 0, k = 0;
1639 int glyphWidth = 0;
1640 for (i = 0; i < length; i++, k++)
1641 {
1642 glyphWidth = 0;
1643 int next = 1;
1644 letter = GetNextCodepoint(&text[i], &next);
1645 if (letter == 0x3f) next = 1;
1646 index = GetGlyphIndex(font, letter);
1647 i += next - 1;
1648
1649 if (letter != '\n')
1650 {
1651 glyphWidth = (font.chars[index].advanceX == 0)?
1652 (int)(font.chars[index].rec.width*scaleFactor + spacing):
1653 (int)(font.chars[index].advanceX*scaleFactor + spacing);
1654
1655 if ((textOffsetX + glyphWidth + 1) >= rec.width) break;
1656
1657 if ((mode == GUI_MEASURE_MODE_CURSOR_POS) && (*pos == k)) break;
1658 else if (mode == GUI_MEASURE_MODE_CURSOR_COORDS)
1659 {
1660 // Check if the mouse pointer is inside the glyph rect
1661 Rectangle grec = {rec.x + textOffsetX - 1, rec.y, glyphWidth, (font.baseSize + font.baseSize/2)*scaleFactor - 1 };
1662 Vector2 mouse = GetMousePosition();
1663
1664 if (CheckCollisionPointRec(mouse, grec))
1665 {
1666 // Smooth selection by dividing the glyph rectangle into 2 equal parts and checking where the mouse resides
1667 if (mouse.x > (grec.x + glyphWidth/2))
1668 {
1669 textOffsetX += glyphWidth;
1670 k++;
1671 }
1672
1673 break;
1674 }
1675 }
1676 }
1677 else break;
1678
1679 textOffsetX += glyphWidth;
1680 }
1681
1682 *pos = k;
1683
1684 return (rec.x + textOffsetX - 1);
1685}
1686
1687static int GetPrevCodepoint(const char *text, const char *start, int *prev)
1688{
1689 int c = 0x3f;
1690 char *p = (char *)text;
1691 *prev = 1;
1692
1693 for (int i = 0; (p >= start) && (i < 4); p--, i++)
1694 {
1695 if ((((unsigned char)*p) >> 6) != 2)
1696 {
1697 c = GetNextCodepoint(p, prev);
1698 break;
1699 }
1700 }
1701
1702 return c;
1703}
1704
1705// Required by GuiTextBoxEx()
1706// Highly synchronized with calculations in DrawTextRecEx()
1707static int GuiMeasureTextBoxRev(const char *text, int length, Rectangle rec, int *pos)
1708{
1709 // Get gui font properties
1710 const Font font = guiFont;
1711 const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE);
1712 const float spacing = GuiGetStyle(DEFAULT, TEXT_SPACING);
1713
1714 int textOffsetX = 0; // Offset between characters
1715 float scaleFactor = 0.0f;
1716
1717 int letter = 0; // Current character
1718 int index = 0; // Index position in sprite font
1719
1720 scaleFactor = fontSize/font.baseSize;
1721
1722 int i = 0, k = 0;
1723 int glyphWidth = 0, prev = 1;
1724 for (i = length; i >= 0; i--, k++)
1725 {
1726 glyphWidth = 0;
1727 letter = GetPrevCodepoint(&text[i], &text[0], &prev);
1728
1729 if (letter == 0x3f) prev = 1;
1730 index = GetGlyphIndex(font, letter);
1731 i -= prev - 1;
1732
1733 if (letter != '\n')
1734 {
1735 glyphWidth = (font.chars[index].advanceX == 0)?
1736 (int)(font.chars[index].rec.width*scaleFactor + spacing):
1737 (int)(font.chars[index].advanceX*scaleFactor + spacing);
1738
1739 if ((textOffsetX + glyphWidth + 1) >= rec.width) break;
1740 }
1741 else break;
1742
1743 textOffsetX += glyphWidth;
1744 }
1745
1746 *pos = k;
1747
1748 return (i + prev);
1749}
1750
1751
1752// Calculate cursor coordinates based on the cursor position `pos` inside the `text`.
1753static inline int GuiTextBoxGetCursorCoordinates(const char *text, int length, Rectangle rec, int pos)
1754{
1755 return GuiMeasureTextBox(text, length, rec, &pos, GUI_MEASURE_MODE_CURSOR_POS);
1756}
1757
1758// Calculate cursor position in textbox based on mouse coordinates.
1759static inline int GuiTextBoxGetCursorFromMouse(const char *text, int length, Rectangle rec, int* pos)
1760{
1761 return GuiMeasureTextBox(text, length, rec, pos, GUI_MEASURE_MODE_CURSOR_COORDS);
1762}
1763
1764// Calculates how many characters is the textbox able to draw inside rec
1765static inline int GuiTextBoxMaxCharacters(const char *text, int length, Rectangle rec)
1766{
1767 int pos = -1;
1768 GuiMeasureTextBox(text, length, rec, &pos, GUI_MEASURE_MODE_CURSOR_END);
1769 return pos;
1770}
1771
1772// Returns total number of characters(codepoints) in a UTF8 encoded `text` until `\0` or a `\n` is found.
1773// NOTE: If a invalid UTF8 sequence is encountered a `?`(0x3f) codepoint is counted instead.
1774static inline unsigned int GuiCountCodepointsUntilNewline(const char *text)
1775{
1776 unsigned int len = 0;
1777 char *ptr = (char*)&text[0];
1778
1779 while ((*ptr != '\0') && (*ptr != '\n'))
1780 {
1781 int next = 0;
1782 int letter = GetNextCodepoint(ptr, &next);
1783
1784 if (letter == 0x3f) ptr += 1;
1785 else ptr += next;
1786 ++len;
1787 }
1788
1789 return len;
1790}
1791
1792static inline void MoveTextBoxCursorRight(const char* text, int length, Rectangle textRec)
1793{
1794 // FIXME: Counting codepoints each time we press the key is expensive, find another way
1795 int count = GuiCountCodepointsUntilNewline(text);
1796 if (guiTextBoxState.cursor < count ) guiTextBoxState.cursor++;
1797
1798 const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec);
1799
1800 if ((guiTextBoxState.cursor - guiTextBoxState.start) > max)
1801 {
1802 const int cidx = GuiTextBoxGetByteIndex(text, guiTextBoxState.index, guiTextBoxState.start, guiTextBoxState.cursor);
1803 int pos = 0;
1804 guiTextBoxState.index = GuiMeasureTextBoxRev(text, cidx - 1, textRec, &pos);
1805 guiTextBoxState.start = guiTextBoxState.cursor - pos;
1806 }
1807}
1808
1809static inline void MoveTextBoxCursorLeft(const char* text)
1810{
1811 if (guiTextBoxState.cursor > 0) guiTextBoxState.cursor--;
1812
1813 if (guiTextBoxState.cursor < guiTextBoxState.start)
1814 {
1815 int prev = 0;
1816 int letter = GetPrevCodepoint(&text[guiTextBoxState.index - 1], text, &prev);
1817 if (letter == 0x3f) prev = 1;
1818 guiTextBoxState.start--;
1819 guiTextBoxState.index -= prev;
1820 }
1821}
1822
1823RAYGUIDEF int GuiTextBoxGetByteIndex(const char *text, int start, int from, int to)
1824{
1825 int i = start, k = from;
1826
1827 while ((text[i] != '\0') && (k < to))
1828 {
1829 int j = 0;
1830 int letter = GetNextCodepoint(&text[i], &j);
1831
1832 if (letter == 0x3f) j = 1;
1833 i += j;
1834 ++k;
1835 }
1836
1837 return i;
1838}
1839
1840RAYGUIDEF int GuiTextBoxDelete(char *text, int length, bool before)
1841{
1842 if ((guiTextBoxState.cursor != -1) && (text != NULL))
1843 {
1844 int startIdx = 0, endIdx = 0;
1845 if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor))
1846 {
1847 // Delete selection
1848 int start = guiTextBoxState.cursor;
1849 int end = guiTextBoxState.select;
1850
1851 if (guiTextBoxState.cursor > guiTextBoxState.select)
1852 {
1853 start = guiTextBoxState.select;
1854 end = guiTextBoxState.cursor;
1855 }
1856
1857 // Convert to byte indexes
1858 startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start);
1859 endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end);
1860
1861 // Adjust text box state
1862 guiTextBoxState.cursor = start; // Always set cursor to start of selection
1863 if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame
1864 }
1865 else
1866 {
1867 if (before)
1868 {
1869 // Delete character before cursor
1870 if (guiTextBoxState.cursor != 0)
1871 {
1872 endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor);
1873 guiTextBoxState.cursor--;
1874 startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor);
1875
1876 if (guiTextBoxState.cursor < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate on the next frame
1877 }
1878 }
1879 else
1880 {
1881 // Delete character after cursor
1882 if (guiTextBoxState.cursor + 1 <= GuiCountCodepointsUntilNewline(text))
1883 {
1884 startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor);
1885 endIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor+1);
1886 }
1887 }
1888 }
1889
1890 memmove(&text[startIdx], &text[endIdx], length - endIdx);
1891 text[length - (endIdx - startIdx)] = '\0';
1892 guiTextBoxState.select = -1; // Always deselect
1893
1894 return (endIdx - startIdx);
1895 }
1896
1897 return 0;
1898}
1899
1900RAYGUIDEF void GuiTextBoxSelectAll(const char *text)
1901{
1902 guiTextBoxState.cursor = GuiCountCodepointsUntilNewline(text);
1903
1904 if (guiTextBoxState.cursor > 0)
1905 {
1906 guiTextBoxState.select = 0;
1907 guiTextBoxState.start = -1; // Force recalculate on the next frame
1908 }
1909 else guiTextBoxState.select = -1;
1910}
1911
1912RAYGUIDEF void GuiTextBoxCopy(const char *text)
1913{
1914 if ((text != NULL) &&
1915 (guiTextBoxState.select != -1) &&
1916 (guiTextBoxState.cursor != -1) &&
1917 (guiTextBoxState.select != guiTextBoxState.cursor))
1918 {
1919 int start = guiTextBoxState.cursor;
1920 int end = guiTextBoxState.select;
1921
1922 if (guiTextBoxState.cursor > guiTextBoxState.select)
1923 {
1924 start = guiTextBoxState.select;
1925 end = guiTextBoxState.cursor;
1926 }
1927
1928 // Convert to byte indexes
1929 start = GuiTextBoxGetByteIndex(text, 0, 0, start);
1930 end = GuiTextBoxGetByteIndex(text, 0, 0, end);
1931
1932 // FIXME: `TextSubtext()` only lets use copy MAX_TEXT_BUFFER_LENGTH (1024) bytes
1933 // maybe modify `SetClipboardText()` so we can use it only on part of a string
1934 const char *clipText = TextSubtext(text, start, end - start);
1935
1936 SetClipboardText(clipText);
1937 }
1938}
1939
1940// Paste text from clipboard into the active textbox.
1941// `text` is the pointer to the buffer used by the textbox while `textSize` is the text buffer max size
1942RAYGUIDEF void GuiTextBoxPaste(char *text, int textSize)
1943{
1944 const char *clipText = GetClipboardText(); // GLFW guaratees this should be UTF8 encoded!
1945 int length = strlen(text);
1946
1947 if ((text != NULL) && (clipText != NULL) && (guiTextBoxState.cursor != -1))
1948 {
1949 if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor))
1950 {
1951 // If there's a selection we'll have to delete it first
1952 length -= GuiTextBoxDelete(text, length, true);
1953 }
1954
1955 int clipLen = strlen(clipText); // We want the length in bytes
1956
1957 // Calculate how many bytes can we copy from clipboard text before we run out of space
1958 int size = ((length + clipLen) <= textSize) ? clipLen : textSize - length;
1959
1960 // Make room by shifting to right the bytes after cursor
1961 int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor);
1962 int endIdx = startIdx + size;
1963 memmove(&text[endIdx], &text[startIdx], length - startIdx);
1964 text[length + size] = '\0'; // Set the NULL char
1965
1966 // At long last copy the clipboard text
1967 memcpy(&text[startIdx], clipText, size);
1968
1969 // Set cursor position at the end of the pasted text
1970 guiTextBoxState.cursor = 0;
1971
1972 for (int i = 0; i < (startIdx + size); guiTextBoxState.cursor++)
1973 {
1974 int next = 0;
1975 int letter = GetNextCodepoint(&text[i], &next);
1976 if (letter != 0x3f) i += next;
1977 else i += 1;
1978 }
1979
1980 guiTextBoxState.start = -1; // Force to recalculate on the next frame
1981 }
1982}
1983
1984RAYGUIDEF void GuiTextBoxCut(char* text)
1985{
1986 if ((text != NULL) &&
1987 (guiTextBoxState.select != -1) &&
1988 (guiTextBoxState.cursor != -1) &&
1989 (guiTextBoxState.select != guiTextBoxState.cursor))
1990 {
1991 // First copy selection to clipboard;
1992 int start = guiTextBoxState.cursor, end = guiTextBoxState.select;
1993
1994 if (guiTextBoxState.cursor > guiTextBoxState.select)
1995 {
1996 start = guiTextBoxState.select;
1997 end = guiTextBoxState.cursor;
1998 }
1999
2000 // Convert to byte indexes
2001 int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, start);
2002 int endIdx = GuiTextBoxGetByteIndex(text, 0, 0, end);
2003
2004 // FIXME: `TextSubtext()` only lets use copy MAX_TEXT_BUFFER_LENGTH (1024) bytes
2005 // maybe modify `SetClipboardText()` so we can use it only on parts of a string
2006 const char *clipText = TextSubtext(text, startIdx, endIdx - startIdx);
2007 SetClipboardText(clipText);
2008
2009 // Now delete selection (copy data over it)
2010 int len = strlen(text);
2011 memmove(&text[startIdx], &text[endIdx], len - endIdx);
2012 text[len - (endIdx - startIdx)] = '\0';
2013
2014 // Adjust text box state
2015 guiTextBoxState.cursor = start; // Always set cursor to start of selection
2016 if (guiTextBoxState.select < guiTextBoxState.start) guiTextBoxState.start = -1; // Force to recalculate
2017 guiTextBoxState.select = -1; // Deselect
2018 }
2019}
2020
2021static int EncodeCodepoint(unsigned int c, char out[5])
2022{
2023 int len = 0;
2024 if (c <= 0x7f)
2025 {
2026 out[0] = (char)c;
2027 len = 1;
2028 }
2029 else if (c <= 0x7ff)
2030 {
2031 out[0] = (char)(((c >> 6) & 0x1f) | 0xc0);
2032 out[1] = (char)((c & 0x3f) | 0x80);
2033 len = 2;
2034 }
2035 else if (c <= 0xffff)
2036 {
2037 out[0] = (char)(((c >> 12) & 0x0f) | 0xe0);
2038 out[1] = (char)(((c >> 6) & 0x3f) | 0x80);
2039 out[2] = (char)((c & 0x3f) | 0x80);
2040 len = 3;
2041 }
2042 else if (c <= 0x10ffff)
2043 {
2044 out[0] = (char)(((c >> 18) & 0x07) | 0xf0);
2045 out[1] = (char)(((c >> 12) & 0x3f) | 0x80);
2046 out[2] = (char)(((c >> 6) & 0x3f) | 0x80);
2047 out[3] = (char)((c & 0x3f) | 0x80);
2048 len = 4;
2049 }
2050
2051 out[len] = 0;
2052 return len;
2053}
2054
2055// A text box control supporting text selection, cursor positioning and commonly used keyboard shortcuts.
2056// NOTE 1: Requires static variables: framesCounter
2057// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation)
2058RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
2059{
2060 // Define the cursor movement/selection speed when movement keys are held/pressed
2061 #define GUI_TEXTBOX_CURSOR_SPEED_MODIFIER 5
2062
2063 static int framesCounter = 0; // Required for blinking cursor
2064
2065 GuiControlState state = guiState;
2066 bool pressed = false;
2067
2068 // Make sure length doesn't exceed `textSize`. `textSize` is actually the max amount of characters the textbox can handle.
2069 int length = strlen(text);
2070 if (length > textSize)
2071 {
2072 text[textSize] = '\0';
2073 length = textSize;
2074 }
2075
2076 // Make sure we have enough room to draw at least 1 character
2077 if ((bounds.width - 2*GuiGetStyle(TEXTBOX, INNER_PADDING)) < GuiGetStyle(DEFAULT, TEXT_SIZE))
2078 {
2079 bounds.width = GuiGetStyle(DEFAULT, TEXT_SIZE) + 2*GuiGetStyle(TEXTBOX, INNER_PADDING);
2080 }
2081
2082 // Center the text vertically
2083 int verticalPadding = (bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) - GuiGetStyle(DEFAULT, TEXT_SIZE))/2;
2084
2085 if (verticalPadding < 0)
2086 {
2087 // Make sure the height is sufficient
2088 bounds.height = 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(DEFAULT, TEXT_SIZE);
2089 verticalPadding = 0;
2090 }
2091
2092 // Calculate the drawing area for the text inside the control `bounds`
2093 Rectangle textRec = { bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING),
2094 bounds.y + verticalPadding + GuiGetStyle(TEXTBOX, BORDER_WIDTH),
2095 bounds.width - 2*(GuiGetStyle(TEXTBOX, INNER_PADDING) + GuiGetStyle(TEXTBOX, BORDER_WIDTH)),
2096 GuiGetStyle(DEFAULT, TEXT_SIZE) };
2097
2098 Vector2 cursorPos = { textRec.x, textRec.y }; // This holds the coordinates inside textRec of the cursor at current position and will be recalculated later
2099 bool active = GuiTextBoxIsActive(bounds); // Check if this textbox is the global active textbox
2100
2101 int selStart = 0, selLength = 0, textStartIndex = 0;
2102
2103 // Update control
2104 //--------------------------------------------------------------------
2105 if ((state != GUI_STATE_DISABLED) && !guiLocked)
2106 {
2107 const Vector2 mousePoint = GetMousePosition();
2108
2109 if (editMode)
2110 {
2111 // Check if we are the global active textbox
2112 // A textbox becomes active when the user clicks it :)
2113 if (!active)
2114 {
2115 if (CheckCollisionPointRec(mousePoint, bounds) &&
2116 (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)))
2117 {
2118 // Hurray!!! we just became the active textbox
2119 active = true;
2120 GuiTextBoxSetActive(bounds);
2121 }
2122 }
2123 else if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_RIGHT_BUTTON))
2124 {
2125 // When active and the right mouse is clicked outside the textbox we should deactivate it
2126 GuiTextBoxSetActive(RAYGUI_CLITERAL(Rectangle){0,0,-1,-1}); // Set a dummy rect as the active textbox bounds
2127 active = false;
2128 }
2129
2130 if (active)
2131 {
2132 state = GUI_STATE_PRESSED;
2133 framesCounter++;
2134
2135 // Make sure state doesn't have invalid values
2136 if (guiTextBoxState.cursor > length) guiTextBoxState.cursor = -1;
2137 if (guiTextBoxState.select > length) guiTextBoxState.select = -1;
2138 if (guiTextBoxState.start > length) guiTextBoxState.start = -1;
2139
2140
2141 // Check textbox state for changes and recalculate if necesary
2142 if (guiTextBoxState.cursor == -1)
2143 {
2144 // Set cursor to last visible character in textbox
2145 guiTextBoxState.cursor = GuiTextBoxMaxCharacters(text, length, textRec);
2146 }
2147
2148 if (guiTextBoxState.start == -1)
2149 {
2150 // Force recalculate text start position and text start index
2151
2152 // NOTE: start and index are always in sync
2153 // start will hold the starting character position from where the text will be drawn
2154 // while index will hold the byte index inside the text for that character
2155
2156 if (guiTextBoxState.cursor == 0)
2157 {
2158 guiTextBoxState.start = guiTextBoxState.index = 0; // No need to recalculate
2159 }
2160 else
2161 {
2162 int pos = 0;
2163 int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor);
2164 guiTextBoxState.index = GuiMeasureTextBoxRev(text, len, textRec, &pos);
2165 guiTextBoxState.start = guiTextBoxState.cursor - pos + 1;
2166 }
2167 }
2168
2169 // -----------------
2170 // HANDLE KEY INPUT
2171 // -----------------
2172 // * -> | LSHIFT + -> move cursor to the right | increase selection by one
2173 // * <- | LSHIFT + <- move cursor to the left | decrease selection by one
2174 // * HOME | LSHIFT + HOME moves cursor to start of text | selects text from cursor to start of text
2175 // * END | LSHIFT + END move cursor to end of text | selects text from cursor until end of text
2176 // * CTRL + A select all characters in text
2177 // * CTRL + C copy selected text
2178 // * CTRL + X cut selected text
2179 // * CTRL + V remove selected text, if any, then paste clipboard data
2180 // * DEL delete character or selection after cursor
2181 // * BACKSPACE delete character or selection before cursor
2182 // TODO: Add more shortcuts (insert mode, select word, moveto/select prev/next word ...)
2183 if (IsKeyPressed(KEY_RIGHT) ||
2184 (IsKeyDown(KEY_RIGHT) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER == 0)))
2185 {
2186 if (IsKeyDown(KEY_LEFT_SHIFT))
2187 {
2188 // Selecting
2189 if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start
2190
2191 MoveTextBoxCursorRight(text, length, textRec);
2192 }
2193 else
2194 {
2195 if (guiTextBoxState.select != -1 && guiTextBoxState.select != guiTextBoxState.cursor)
2196 {
2197 // Deselect and move cursor to end of selection
2198 if (guiTextBoxState.cursor < guiTextBoxState.select)
2199 {
2200 guiTextBoxState.cursor = guiTextBoxState.select - 1;
2201 MoveTextBoxCursorRight(text, length, textRec);
2202 }
2203 }
2204 else
2205 {
2206 // Move cursor to the right
2207 MoveTextBoxCursorRight(text, length, textRec);
2208 }
2209
2210 guiTextBoxState.select = -1;
2211 }
2212
2213 framesCounter = 0;
2214 }
2215 else if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER == 0)))
2216 {
2217 if (IsKeyDown(KEY_LEFT_SHIFT))
2218 {
2219 // Selecting
2220 if (guiTextBoxState.select == -1) guiTextBoxState.select = guiTextBoxState.cursor; // Mark selection start
2221
2222 MoveTextBoxCursorLeft(text);
2223 }
2224 else
2225 {
2226 if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor))
2227 {
2228 // Deselect and move cursor to start of selection
2229 if (guiTextBoxState.cursor > guiTextBoxState.select)
2230 {
2231 guiTextBoxState.cursor = guiTextBoxState.select;
2232
2233 if (guiTextBoxState.start > guiTextBoxState.cursor)
2234 {
2235 guiTextBoxState.start = guiTextBoxState.cursor;
2236 guiTextBoxState.index = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.start); // Recalculate byte index
2237 }
2238 }
2239 }
2240 else
2241 {
2242 // Move cursor to the left
2243 MoveTextBoxCursorLeft(text);
2244 }
2245
2246 guiTextBoxState.select = -1;
2247 }
2248
2249 framesCounter = 0;
2250 }
2251 else if (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0))
2252 {
2253 GuiTextBoxDelete(text, length, true);
2254 }
2255 else if (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0))
2256 {
2257 GuiTextBoxDelete(text, length, false);
2258 }
2259 else if (IsKeyPressed(KEY_HOME))
2260 {
2261 if (IsKeyDown(KEY_LEFT_SHIFT))
2262 {
2263 // Select from start of text to cursor
2264 if ((guiTextBoxState.select > guiTextBoxState.cursor) ||
2265 ((guiTextBoxState.select == -1) && (guiTextBoxState.cursor != 0)))
2266 {
2267 guiTextBoxState.select = guiTextBoxState.cursor;
2268 }
2269 }
2270 else guiTextBoxState.select = -1; // Deselect everything
2271
2272 // Move cursor to start of text
2273 guiTextBoxState.cursor = guiTextBoxState.start = guiTextBoxState.index = 0;
2274 framesCounter = 0;
2275 }
2276 else if (IsKeyPressed(KEY_END))
2277 {
2278 int max = GuiCountCodepointsUntilNewline(text);
2279
2280 if (IsKeyDown(KEY_LEFT_SHIFT))
2281 {
2282 if ((guiTextBoxState.select == -1) && (guiTextBoxState.cursor != max))
2283 {
2284 guiTextBoxState.select = guiTextBoxState.cursor;
2285 }
2286 }
2287 else guiTextBoxState.select = -1; // Deselect everything
2288
2289 int pos = 0;
2290 guiTextBoxState.cursor = max;
2291 int len = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor);
2292 guiTextBoxState.index = GuiMeasureTextBoxRev(text, len, textRec, &pos);
2293 guiTextBoxState.start = guiTextBoxState.cursor - pos + 1;
2294 }
2295 else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_A))
2296 {
2297 // `CTRL + A` Select all
2298 GuiTextBoxSelectAll(text);
2299 }
2300 else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C))
2301 {
2302 // `CTRL + C` Copy selected text to clipboard
2303 GuiTextBoxCopy(text);
2304 }
2305 else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_X))
2306 {
2307 // `CTRL + X` Cut selected text
2308 GuiTextBoxCut(text);
2309 }
2310 else if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V))
2311 {
2312 // `CTRL + V` Paste clipboard text
2313 GuiTextBoxPaste(text, textSize);
2314 }
2315 else if (IsKeyPressed(KEY_ENTER))
2316 {
2317 pressed = true;
2318 }
2319 else
2320 {
2321 int key = GetKeyPressed();
2322 if ((key >= 32) && ((guiTextBoxState.cursor + 1) < textSize))
2323 {
2324 if ((guiTextBoxState.select != -1) && (guiTextBoxState.select != guiTextBoxState.cursor))
2325 {
2326 // Delete selection
2327 GuiTextBoxDelete(text, length, true);
2328 }
2329
2330 // Decode codepoint
2331 char out[5] = {0};
2332 int sz = EncodeCodepoint(key, &out[0]);
2333
2334 if (sz != 0)
2335 {
2336 int startIdx = GuiTextBoxGetByteIndex(text, 0, 0, guiTextBoxState.cursor);
2337 int endIdx = startIdx + sz;
2338
2339 if (endIdx <= textSize && length < textSize - 1)
2340 {
2341 guiTextBoxState.cursor++;
2342 guiTextBoxState.select = -1;
2343 memmove(&text[endIdx], &text[startIdx], length - startIdx);
2344 memcpy(&text[startIdx], &out[0], sz);
2345 length += sz;
2346 text[length] = '\0';
2347
2348 if (guiTextBoxState.start != -1)
2349 {
2350 const int max = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec);
2351
2352 if ((guiTextBoxState.cursor - guiTextBoxState.start) > max) guiTextBoxState.start = -1;
2353 }
2354 }
2355 }
2356 }
2357 }
2358
2359 // -------------
2360 // HANDLE MOUSE
2361 // -------------
2362 if (CheckCollisionPointRec(mousePoint, bounds))
2363 {
2364 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
2365 {
2366 if (CheckCollisionPointRec(mousePoint, textRec))
2367 {
2368 GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, &guiTextBoxState.cursor);
2369 guiTextBoxState.cursor += guiTextBoxState.start;
2370 guiTextBoxState.select = -1;
2371 }
2372 else
2373 {
2374 // Clicked outside the `textRec` but still inside bounds
2375 if (mousePoint.x <= bounds.x+bounds.width/2) guiTextBoxState.cursor = 0 + guiTextBoxState.start;
2376 else guiTextBoxState.cursor = guiTextBoxState.start + GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec);
2377 guiTextBoxState.select = -1;
2378 }
2379 }
2380 else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
2381 {
2382 int cursor = guiTextBoxState.cursor - guiTextBoxState.start;
2383 bool move = false;
2384 if (CheckCollisionPointRec(mousePoint, textRec))
2385 {
2386 GuiTextBoxGetCursorFromMouse(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, &cursor);
2387 }
2388 else
2389 {
2390 // Clicked outside the `textRec` but still inside bounds, this means that we must move the text
2391 move = true;
2392 if (mousePoint.x > bounds.x+bounds.width/2)
2393 {
2394 cursor = GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec);
2395 }
2396 }
2397
2398 guiTextBoxState.cursor = cursor + guiTextBoxState.start;
2399
2400 if (guiTextBoxState.select == -1)
2401 {
2402 // Mark start of selection
2403 guiTextBoxState.select = guiTextBoxState.cursor;
2404 }
2405
2406 // Move the text when cursor is positioned before or after the text
2407 if ((framesCounter%GUI_TEXTBOX_CURSOR_SPEED_MODIFIER) == 0 && move)
2408 {
2409 if (cursor == 0) MoveTextBoxCursorLeft(text);
2410 else if (cursor == GuiTextBoxMaxCharacters(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec))
2411 {
2412 MoveTextBoxCursorRight(text, length, textRec);
2413 }
2414 }
2415 }
2416 }
2417
2418 // Calculate X coordinate of the blinking cursor
2419 cursorPos.x = GuiTextBoxGetCursorCoordinates(&text[guiTextBoxState.index], length - guiTextBoxState.index, textRec, guiTextBoxState.cursor - guiTextBoxState.start);
2420
2421 // Update variables
2422 textStartIndex = guiTextBoxState.index;
2423
2424 if (guiTextBoxState.select == -1)
2425 {
2426 selStart = guiTextBoxState.cursor;
2427 selLength = 0;
2428 }
2429 else if (guiTextBoxState.cursor > guiTextBoxState.select)
2430 {
2431 selStart = guiTextBoxState.select;
2432 selLength = guiTextBoxState.cursor - guiTextBoxState.select;
2433 }
2434 else
2435 {
2436 selStart = guiTextBoxState.cursor;
2437 selLength = guiTextBoxState.select - guiTextBoxState.cursor;
2438 }
2439
2440 // We aren't drawing all of the text so make sure `DrawTextRecEx()` is selecting things correctly
2441 if (guiTextBoxState.start > selStart)
2442 {
2443 selLength -= guiTextBoxState.start - selStart;
2444 selStart = 0;
2445 }
2446 else selStart = selStart - guiTextBoxState.start;
2447 }
2448 else state = GUI_STATE_FOCUSED;
2449 }
2450 else
2451 {
2452 if (CheckCollisionPointRec(mousePoint, bounds))
2453 {
2454 state = GUI_STATE_FOCUSED;
2455 if (IsMouseButtonPressed(0)) pressed = true;
2456 }
2457
2458 if (active && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C))
2459 {
2460 // If active copy all text to clipboard even when disabled
2461
2462 // Backup textbox state
2463 int select = guiTextBoxState.select;
2464 int cursor = guiTextBoxState.cursor;
2465 int start = guiTextBoxState.start;
2466 if (guiTextBoxState.select == -1 || guiTextBoxState.select == guiTextBoxState.cursor)
2467 {
2468 // If no selection then mark all text to be copied to clipboard
2469 GuiTextBoxSelectAll(text);
2470 }
2471
2472 GuiTextBoxCopy(text);
2473
2474 // Restore textbox state
2475 guiTextBoxState.select = select;
2476 guiTextBoxState.cursor = cursor;
2477 guiTextBoxState.start = start;
2478 }
2479 }
2480 }
2481
2482 // Draw control
2483 //--------------------------------------------------------------------
2484 DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha));
2485
2486 if (state == GUI_STATE_PRESSED)
2487 {
2488 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));
2489 if (editMode && active && ((framesCounter/TEXTEDIT_CURSOR_BLINK_FRAMES)%2 == 0) && selLength == 0)
2490 {
2491 // Draw the blinking cursor
2492 DrawRectangle(cursorPos.x, cursorPos.y, 1, GuiGetStyle(DEFAULT, TEXT_SIZE), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
2493 }
2494 }
2495 else if (state == GUI_STATE_DISABLED)
2496 {
2497 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));
2498 }
2499
2500 // Finally draw the text and selection
2501 DrawTextRecEx(guiFont, &text[textStartIndex], textRec, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), false, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha), selStart, selLength, GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_FG)), GetColor(GuiGetStyle(TEXTBOX, COLOR_SELECTED_BG)));
2502
2503 return pressed;
2504}
2505#else // !RAYGUI_TEXTBOX_EXTENDED
2506
2507// Spinner control, returns selected value
2508// NOTE: Requires static variables: framesCounter, valueSpeed - ERROR!
2509RAYGUIDEF bool GuiSpinner(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode)
2510{
2511 bool pressed = false;
2512 int tempValue = *value;
2513
2514 Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING), bounds.y,
2515 bounds.width - 2*(GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING)), bounds.height };
2516 Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.height };
2517 Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH), (float)bounds.height };
2518
2519 // Update control
2520 //--------------------------------------------------------------------
2521 if (!editMode)
2522 {
2523 if (tempValue < minValue) tempValue = minValue;
2524 if (tempValue > maxValue) tempValue = maxValue;
2525 }
2526 //--------------------------------------------------------------------
2527
2528 // Draw control
2529 //--------------------------------------------------------------------
2530 // TODO: Set Spinner properties for ValueBox
2531 pressed = GuiValueBox(spinner, &tempValue, minValue, maxValue, editMode);
2532
2533 // Draw value selector custom buttons
2534 // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
2535 int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
2536 GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
2537
2538 int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
2539 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
2540
2541#if defined(RAYGUI_RICONS_SUPPORT)
2542 if (GuiButton(leftButtonBound, GuiIconText(RICON_ARROW_LEFT_FILL, NULL))) tempValue--;
2543 if (GuiButton(rightButtonBound, GuiIconText(RICON_ARROW_RIGHT_FILL, NULL))) tempValue++;
2544#else
2545 if (GuiButton(leftButtonBound, "<")) tempValue--;
2546 if (GuiButton(rightButtonBound, ">")) tempValue++;
2547#endif
2548
2549 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
2550 GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
2551 //--------------------------------------------------------------------
2552
2553 *value = tempValue;
2554 return pressed;
2555}
2556
2557// Value Box control, updates input text with numbers
2558// NOTE: Requires static variables: framesCounter
2559RAYGUIDEF bool GuiValueBox(Rectangle bounds, int *value, int minValue, int maxValue, bool editMode)
2560{
2561 #define VALUEBOX_MAX_CHARS 32
2562
2563 static int framesCounter = 0; // Required for blinking cursor
2564
2565 GuiControlState state = guiState;
2566 bool pressed = false;
2567
2568 char text[VALUEBOX_MAX_CHARS + 1] = "\0";
2569 sprintf(text, "%i", *value);
2570
2571 // Update control
2572 //--------------------------------------------------------------------
2573 if ((state != GUI_STATE_DISABLED) && !guiLocked)
2574 {
2575 Vector2 mousePoint = GetMousePosition();
2576
2577 bool valueHasChanged = false;
2578
2579 if (editMode)
2580 {
2581 state = GUI_STATE_PRESSED;
2582
2583 framesCounter++;
2584
2585 int keyCount = strlen(text);
2586
2587 // Only allow keys in range [48..57]
2588 if (keyCount < VALUEBOX_MAX_CHARS)
2589 {
2590 int maxWidth = (bounds.width - (GuiGetStyle(VALUEBOX, INNER_PADDING)*2));
2591 if (GetTextWidth(text) < maxWidth)
2592 {
2593 int key = GetKeyPressed();
2594 if ((key >= 48) && (key <= 57))
2595 {
2596 text[keyCount] = (char)key;
2597 keyCount++;
2598 valueHasChanged = true;
2599 }
2600 }
2601 }
2602
2603 // Delete text
2604 if (keyCount > 0)
2605 {
2606 if (IsKeyPressed(KEY_BACKSPACE))
2607 {
2608 keyCount--;
2609 text[keyCount] = '\0';
2610 framesCounter = 0;
2611 if (keyCount < 0) keyCount = 0;
2612 valueHasChanged = true;
2613 }
2614 else if (IsKeyDown(KEY_BACKSPACE))
2615 {
2616 if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--;
2617 text[keyCount] = '\0';
2618 if (keyCount < 0) keyCount = 0;
2619 valueHasChanged = true;
2620 }
2621 }
2622
2623 if (valueHasChanged) *value = atoi(text);
2624 }
2625 else
2626 {
2627 if (*value > maxValue) *value = maxValue;
2628 else if (*value < minValue) *value = minValue;
2629 }
2630
2631 if (!editMode)
2632 {
2633 if (CheckCollisionPointRec(mousePoint, bounds))
2634 {
2635 state = GUI_STATE_FOCUSED;
2636 if (IsMouseButtonPressed(0)) pressed = true;
2637 }
2638 }
2639 else
2640 {
2641 if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) pressed = true;
2642 }
2643
2644 if (pressed) framesCounter = 0;
2645 }
2646 //--------------------------------------------------------------------
2647
2648 // Draw control
2649 //--------------------------------------------------------------------
2650 DrawRectangleLinesEx(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha));
2651
2652 if (state == GUI_STATE_PRESSED)
2653 {
2654 DrawRectangle(bounds.x + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)), guiAlpha));
2655
2656 if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GetTextWidth(text)/2 + bounds.width/2 + 2, bounds.y + GuiGetStyle(VALUEBOX, INNER_PADDING), 1, bounds.height - GuiGetStyle(VALUEBOX, INNER_PADDING)*2, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha));
2657 }
2658 else if (state == GUI_STATE_DISABLED)
2659 {
2660 DrawRectangle(bounds.x + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)), guiAlpha));
2661 }
2662
2663 GuiDrawText(text, GetTextBounds(VALUEBOX, bounds), GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha));
2664 //--------------------------------------------------------------------
2665
2666 return pressed;
2667}
2668
2669// Text Box control, updates input text
2670// NOTE 1: Requires static variables: framesCounter
2671// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation)
2672RAYGUIDEF bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
2673{
2674 static int framesCounter = 0; // Required for blinking cursor
2675
2676 GuiControlState state = guiState;
2677 bool pressed = false;
2678
2679 // Update control
2680 //--------------------------------------------------------------------
2681 if ((state != GUI_STATE_DISABLED) && !guiLocked)
2682 {
2683 Vector2 mousePoint = GetMousePosition();
2684
2685 if (editMode)
2686 {
2687 state = GUI_STATE_PRESSED;
2688 framesCounter++;
2689
2690 int key = GetKeyPressed();
2691 int keyCount = strlen(text);
2692
2693 // Only allow keys in range [32..125]
2694 if (keyCount < (textSize - 1))
2695 {
2696 int maxWidth = (bounds.width - (GuiGetStyle(DEFAULT, INNER_PADDING)*2));
2697
2698 if (GetTextWidth(text) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE)))
2699 {
2700 if (((key >= 32) && (key <= 125)) ||
2701 ((key >= 128) && (key < 255)))
2702 {
2703 text[keyCount] = (char)key;
2704 keyCount++;
2705 text[keyCount] = '\0';
2706 }
2707 }
2708 }
2709
2710 // Delete text
2711 if (keyCount > 0)
2712 {
2713 if (IsKeyPressed(KEY_BACKSPACE))
2714 {
2715 keyCount--;
2716 text[keyCount] = '\0';
2717 framesCounter = 0;
2718 if (keyCount < 0) keyCount = 0;
2719 }
2720 else if (IsKeyDown(KEY_BACKSPACE))
2721 {
2722 if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--;
2723 text[keyCount] = '\0';
2724 if (keyCount < 0) keyCount = 0;
2725 }
2726 }
2727 }
2728
2729 if (!editMode)
2730 {
2731 if (CheckCollisionPointRec(mousePoint, bounds))
2732 {
2733 state = GUI_STATE_FOCUSED;
2734 if (IsMouseButtonPressed(0)) pressed = true;
2735 }
2736 }
2737 else
2738 {
2739 if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) pressed = true;
2740 }
2741
2742 if (pressed) framesCounter = 0;
2743 }
2744 //--------------------------------------------------------------------
2745
2746 // Draw control
2747 //--------------------------------------------------------------------
2748 DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha));
2749
2750 if (state == GUI_STATE_PRESSED)
2751 {
2752 DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
2753
2754 // Draw blinking cursor
2755 if (editMode && ((framesCounter/20)%2 == 0)) DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, INNER_PADDING) + GetTextWidth(text) + 2 + bounds.width/2*GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), 1, GuiGetStyle(DEFAULT, TEXT_SIZE)*2, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
2756 }
2757 else if (state == GUI_STATE_DISABLED)
2758 {
2759 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));
2760 }
2761
2762 GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha));
2763 //--------------------------------------------------------------------
2764
2765 return pressed;
2766}
2767#endif
2768
2769// Text Box control with multiple lines
2770RAYGUIDEF bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode)
2771{
2772 static int framesCounter = 0; // Required for blinking cursor
2773
2774 GuiControlState state = guiState;
2775 bool pressed = false;
2776
2777 bool textHasChange = false;
2778 int currentLine = 0;
2779 //const char *numChars = NULL;
2780
2781 // Update control
2782 //--------------------------------------------------------------------
2783 if ((state != GUI_STATE_DISABLED) && !guiLocked)
2784 {
2785 Vector2 mousePoint = GetMousePosition();
2786
2787 if (editMode)
2788 {
2789 state = GUI_STATE_PRESSED;
2790
2791 framesCounter++;
2792
2793 int keyCount = strlen(text);
2794 int maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2));
2795 int maxHeight = (bounds.height - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2));
2796
2797 //numChars = TextFormat("%i/%i", keyCount, textSize - 1);
2798
2799 // Only allow keys in range [32..125]
2800 if (keyCount < (textSize - 1))
2801 {
2802 int key = GetKeyPressed();
2803
2804 if (MeasureTextEx(guiFont, text, GuiGetStyle(DEFAULT, TEXT_SIZE), 1).y < (maxHeight - GuiGetStyle(DEFAULT, TEXT_SIZE)))
2805 {
2806 if (IsKeyPressed(KEY_ENTER))
2807 {
2808 text[keyCount] = '\n';
2809 keyCount++;
2810 }
2811 else if (((key >= 32) && (key <= 125)) ||
2812 ((key >= 128) && (key < 255)))
2813 {
2814 text[keyCount] = (char)key;
2815 keyCount++;
2816 textHasChange = true;
2817 }
2818 }
2819 else if (GetTextWidth(strrchr(text, '\n')) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE)))
2820 {
2821 if (((key >= 32) && (key <= 125)) ||
2822 ((key >= 128) && (key < 255)))
2823 {
2824 text[keyCount] = (char)key;
2825 keyCount++;
2826 textHasChange = true;
2827 }
2828 }
2829 }
2830
2831 // Delete text
2832 if (keyCount > 0)
2833 {
2834 if (IsKeyPressed(KEY_BACKSPACE))
2835 {
2836 keyCount--;
2837 text[keyCount] = '\0';
2838 framesCounter = 0;
2839 if (keyCount < 0) keyCount = 0;
2840 textHasChange = true;
2841 }
2842 else if (IsKeyDown(KEY_BACKSPACE))
2843 {
2844 if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--;
2845 text[keyCount] = '\0';
2846 if (keyCount < 0) keyCount = 0;
2847 textHasChange = true;
2848 }
2849 }
2850
2851 // Introduce automatic new line if necessary
2852 if (textHasChange)
2853 {
2854 textHasChange = false;
2855
2856 char *lastLine = strrchr(text, '\n');
2857 int maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, INNER_PADDING)*2));
2858
2859 if (lastLine != NULL)
2860 {
2861 if (GetTextWidth(lastLine) > maxWidth)
2862 {
2863 int firstIndex = lastLine - text;
2864
2865 char *lastSpace = strrchr(lastLine, 32);
2866
2867 if (lastSpace != NULL)
2868 {
2869 int secondIndex = lastSpace - lastLine;
2870 text[firstIndex + secondIndex] = '\n';
2871 }
2872 else
2873 {
2874 int len = (lastLine != NULL)? strlen(lastLine) : 0;
2875 char lastChar = lastLine[len - 1];
2876 lastLine[len - 1] = '\n';
2877 lastLine[len] = lastChar;
2878 lastLine[len + 1] = '\0';
2879 keyCount++;
2880 }
2881 }
2882 }
2883 else
2884 {
2885 if (GetTextWidth(text) > maxWidth)
2886 {
2887 char *lastSpace = strrchr(text, 32);
2888
2889 if (lastSpace != NULL)
2890 {
2891 int index = lastSpace - text;
2892 text[index] = '\n';
2893 }
2894 else
2895 {
2896 int len = (lastLine != NULL)? strlen(lastLine) : 0;
2897 char lastChar = lastLine[len - 1];
2898 lastLine[len - 1] = '\n';
2899 lastLine[len] = lastChar;
2900 lastLine[len + 1] = '\0';
2901 keyCount++;
2902 }
2903 }
2904 }
2905 }
2906
2907 // Counting how many new lines
2908 for (int i = 0; i < keyCount; i++)
2909 {
2910 if (text[i] == '\n') currentLine++;
2911 }
2912 }
2913
2914 // Changing edit mode
2915 if (!editMode)
2916 {
2917 if (CheckCollisionPointRec(mousePoint, bounds))
2918 {
2919 state = GUI_STATE_FOCUSED;
2920 if (IsMouseButtonPressed(0)) pressed = true;
2921 }
2922 }
2923 else
2924 {
2925 if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0)) pressed = true;
2926 }
2927
2928 if (pressed) framesCounter = 0;
2929 }
2930 //--------------------------------------------------------------------
2931
2932 // Draw control
2933 //--------------------------------------------------------------------
2934 DrawRectangleLinesEx(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha));
2935
2936 if (state == GUI_STATE_PRESSED)
2937 {
2938 DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), bounds.height - 2*GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
2939
2940 if (editMode)
2941 {
2942 if ((framesCounter/20)%2 == 0)
2943 {
2944 char *line = NULL;
2945 if (currentLine > 0) line = strrchr(text, '\n');
2946 else line = text;
2947
2948 // Draw text cursor
2949 DrawRectangle(bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING) + GetTextWidth(line),
2950 bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, INNER_PADDING)/2 + ((GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(TEXTBOX, INNER_PADDING))*currentLine),
2951 1, GuiGetStyle(DEFAULT, TEXT_SIZE) + GuiGetStyle(TEXTBOX, INNER_PADDING), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_FOCUSED)), guiAlpha));
2952 }
2953
2954 // Draw characters counter
2955 //GuiDrawText(numChars, RAYGUI_CLITERAL(Vector2){ bounds.x + bounds.width - GetTextWidth(numChars) - GuiGetStyle(TEXTBOX, INNER_PADDING), bounds.y + bounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE) - GuiGetStyle(TEXTBOX, INNER_PADDING) }, Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT_COLOR_PRESSED)), guiAlpha/2));
2956 }
2957 }
2958 else if (state == GUI_STATE_DISABLED)
2959 {
2960 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));
2961 }
2962
2963 GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha));
2964 //--------------------------------------------------------------------
2965
2966 return pressed;
2967}
2968
2969// Slider control with pro parameters
2970// NOTE: Other GuiSlider*() controls use this one
2971RAYGUIDEF float GuiSliderPro(Rectangle bounds, const char *text, float value, float minValue, float maxValue, int sliderWidth, bool showValue)
2972{
2973 GuiControlState state = guiState;
2974
2975 int sliderValue = (int)(((value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
2976
2977 Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, INNER_PADDING),
2978 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, INNER_PADDING) };
2979
2980 if (sliderWidth > 0) // Slider
2981 {
2982 slider.x += (sliderValue - sliderWidth/2);
2983 slider.width = sliderWidth;
2984 }
2985 else if (sliderWidth == 0) // SliderBar
2986 {
2987 slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
2988 slider.width = sliderValue;
2989 }
2990
2991 Rectangle textBounds = { 0 };
2992 textBounds.width = GetTextWidth(text); // TODO: Consider text icon
2993 textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE);
2994 textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
2995 textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
2996
2997 // Update control
2998 //--------------------------------------------------------------------
2999 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3000 {
3001 Vector2 mousePoint = GetMousePosition();
3002
3003 if (CheckCollisionPointRec(mousePoint, bounds))
3004 {
3005 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
3006 {
3007 state = GUI_STATE_PRESSED;
3008
3009 // Get equivalent value and slider position from mousePoint.x
3010 value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
3011
3012 if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider
3013 else if (sliderWidth == 0) slider.width = sliderValue; // SliderBar
3014 }
3015 else state = GUI_STATE_FOCUSED;
3016 }
3017
3018 if (value > maxValue) value = maxValue;
3019 else if (value < minValue) value = minValue;
3020 }
3021
3022 // Bar limits check
3023 if (sliderWidth > 0) // Slider
3024 {
3025 if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
3026 else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
3027 }
3028 else if (sliderWidth == 0) // SliderBar
3029 {
3030 if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
3031 }
3032 //--------------------------------------------------------------------
3033
3034 // Draw control
3035 //--------------------------------------------------------------------
3036 DrawRectangleLinesEx(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha));
3037 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));
3038
3039 // Draw slider internal bar (depends on state)
3040 if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha));
3041 else if (state == GUI_STATE_FOCUSED) DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha));
3042
3043 GuiDrawText(text, textBounds, GuiGetStyle(SLIDER, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha));
3044
3045 // TODO: Review showValue parameter, really ugly...
3046 if (showValue) GuiDrawText(TextFormat("%.02f", value), RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING),
3047 (float)bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING),
3048 (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, GUI_TEXT_ALIGN_LEFT,
3049 Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha));
3050 //--------------------------------------------------------------------
3051
3052 return value;
3053}
3054
3055// Slider control extended, returns selected value and has text
3056RAYGUIDEF float GuiSlider(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue)
3057{
3058 return GuiSliderPro(bounds, text, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH), showValue);
3059}
3060
3061// Slider Bar control extended, returns selected value
3062RAYGUIDEF float GuiSliderBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue)
3063{
3064 return GuiSliderPro(bounds, text, value, minValue, maxValue, 0, showValue);
3065}
3066
3067// Progress Bar control extended, shows current progress value
3068RAYGUIDEF float GuiProgressBar(Rectangle bounds, const char *text, float value, float minValue, float maxValue, bool showValue)
3069{
3070 GuiControlState state = guiState;
3071
3072 Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
3073 bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, INNER_PADDING), 0,
3074 bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, INNER_PADDING) };
3075
3076 // Update control
3077 //--------------------------------------------------------------------
3078 if (state != GUI_STATE_DISABLED) progress.width = (int)(value/(maxValue - minValue)*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)));
3079 //--------------------------------------------------------------------
3080
3081 // Draw control
3082 //--------------------------------------------------------------------
3083 if (showValue) GuiLabel(RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING), (float)bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2 + GuiGetStyle(SLIDER, INNER_PADDING), (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, TextFormat("%.02f", value));
3084
3085 DrawRectangleLinesEx(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha));
3086
3087 // Draw slider internal progress bar (depends on state)
3088 if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED)) DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha));
3089 else if (state == GUI_STATE_FOCUSED) DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha));
3090 //--------------------------------------------------------------------
3091
3092 return value;
3093}
3094
3095// Status Bar control
3096RAYGUIDEF void GuiStatusBar(Rectangle bounds, const char *text)
3097{
3098 GuiControlState state = guiState;
3099
3100 // Draw control
3101 //--------------------------------------------------------------------
3102 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha));
3103 DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH)*2 }, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha));
3104
3105 GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha));
3106 //--------------------------------------------------------------------
3107}
3108
3109// Dummy rectangle control, intended for placeholding
3110RAYGUIDEF void GuiDummyRec(Rectangle bounds, const char *text)
3111{
3112 GuiControlState state = guiState;
3113
3114 // Update control
3115 //--------------------------------------------------------------------
3116 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3117 {
3118 Vector2 mousePoint = GetMousePosition();
3119
3120 // Check button state
3121 if (CheckCollisionPointRec(mousePoint, bounds))
3122 {
3123 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
3124 else state = GUI_STATE_FOCUSED;
3125 }
3126 }
3127 //--------------------------------------------------------------------
3128
3129 // Draw control
3130 //--------------------------------------------------------------------
3131 DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha));
3132
3133 GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != GUI_STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha));
3134 //------------------------------------------------------------------
3135}
3136
3137// Scroll Bar control
3138RAYGUIDEF int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
3139{
3140 GuiControlState state = guiState;
3141
3142 // Is the scrollbar horizontal or vertical?
3143 bool isVertical = (bounds.width > bounds.height)? false : true;
3144
3145 // The size (width or height depending on scrollbar type) of the spinner buttons
3146 const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)? (isVertical? bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0;
3147
3148 // Arrow buttons [<] [>] [∧] [∨]
3149 Rectangle arrowUpLeft = { 0 };
3150 Rectangle arrowDownRight = { 0 };
3151
3152 // Actual area of the scrollbar excluding the arrow buttons
3153 Rectangle scrollbar = { 0 };
3154
3155 // Slider bar that moves --[///]-----
3156 Rectangle slider = { 0 };
3157
3158 // Normalize value
3159 if (value > maxValue) value = maxValue;
3160 if (value < minValue) value = minValue;
3161
3162 const int range = maxValue - minValue;
3163 int sliderSize = GuiGetStyle(SCROLLBAR, SLIDER_SIZE);
3164
3165 // Calculate rectangles for all of the components
3166 arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
3167
3168 if (isVertical)
3169 {
3170 arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize};
3171 scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) };
3172 sliderSize = (sliderSize >= scrollbar.height)? (scrollbar.height - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar
3173 slider = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING), (float)scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)), (float)bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)), (float)sliderSize };
3174 }
3175 else
3176 {
3177 arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize};
3178 scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, INNER_PADDING))};
3179 sliderSize = (sliderSize >= scrollbar.width)? (scrollbar.width - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar
3180 slider = RAYGUI_CLITERAL(Rectangle){ (float)scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING), (float)sliderSize, (float)bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SLIDER_PADDING)) };
3181 }
3182
3183 // Update control
3184 //--------------------------------------------------------------------
3185 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3186 {
3187 Vector2 mousePoint = GetMousePosition();
3188
3189 if (CheckCollisionPointRec(mousePoint, bounds))
3190 {
3191 state = GUI_STATE_FOCUSED;
3192
3193 // Handle mouse wheel
3194 int wheel = GetMouseWheelMove();
3195 if (wheel != 0) value += wheel;
3196
3197 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
3198 {
3199 if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
3200 else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
3201
3202 state = GUI_STATE_PRESSED;
3203 }
3204 else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
3205 {
3206 if (!isVertical)
3207 {
3208 Rectangle scrollArea = { arrowUpLeft.x + arrowUpLeft.width, arrowUpLeft.y, scrollbar.width, bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)};
3209 if (CheckCollisionPointRec(mousePoint, scrollArea)) value = ((float)(mousePoint.x - scrollArea.x - slider.width/2)*range)/(scrollArea.width - slider.width) + minValue;
3210 }
3211 else
3212 {
3213 Rectangle scrollArea = { arrowUpLeft.x, arrowUpLeft.y+arrowUpLeft.height, bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), scrollbar.height};
3214 if (CheckCollisionPointRec(mousePoint, scrollArea)) value = ((float)(mousePoint.y - scrollArea.y - slider.height/2)*range)/(scrollArea.height - slider.height) + minValue;
3215 }
3216 }
3217 }
3218
3219 // Normalize value
3220 if (value > maxValue) value = maxValue;
3221 if (value < minValue) value = minValue;
3222 }
3223 //--------------------------------------------------------------------
3224
3225 // Draw control
3226 //--------------------------------------------------------------------
3227 DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background
3228 DrawRectangleRec(scrollbar, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background
3229
3230 DrawRectangleLinesEx(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha));
3231
3232 DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar
3233
3234 // Draw arrows
3235 const int padding = (spinnerSize - GuiGetStyle(SCROLLBAR, ARROWS_SIZE))/2;
3236 const Vector2 lineCoords[] =
3237 {
3238 // Coordinates for < 0,1,2
3239 { arrowUpLeft.x + padding, arrowUpLeft.y + spinnerSize/2 },
3240 { arrowUpLeft.x + spinnerSize - padding, arrowUpLeft.y + padding },
3241 { arrowUpLeft.x + spinnerSize - padding, arrowUpLeft.y + spinnerSize - padding },
3242
3243 // Coordinates for > 3,4,5
3244 { arrowDownRight.x + padding, arrowDownRight.y + padding },
3245 { arrowDownRight.x + spinnerSize - padding, arrowDownRight.y + spinnerSize/2 },
3246 { arrowDownRight.x + padding, arrowDownRight.y + spinnerSize - padding },
3247
3248 // Coordinates for ∧ 6,7,8
3249 { arrowUpLeft.x + spinnerSize/2, arrowUpLeft.y + padding },
3250 { arrowUpLeft.x + padding, arrowUpLeft.y + spinnerSize - padding },
3251 { arrowUpLeft.x + spinnerSize - padding, arrowUpLeft.y + spinnerSize - padding },
3252
3253 // Coordinates for ∨ 9,10,11
3254 { arrowDownRight.x + padding, arrowDownRight.y + padding },
3255 { arrowDownRight.x + spinnerSize/2, arrowDownRight.y + spinnerSize - padding },
3256 { arrowDownRight.x + spinnerSize - padding, arrowDownRight.y + padding }
3257 };
3258
3259 Color lineColor = Fade(GetColor(GuiGetStyle(BUTTON, TEXT + state*3)), guiAlpha);
3260
3261 if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE))
3262 {
3263 if (isVertical)
3264 {
3265 DrawTriangle(lineCoords[6], lineCoords[7], lineCoords[8], lineColor);
3266 DrawTriangle(lineCoords[9], lineCoords[10], lineCoords[11], lineColor);
3267 }
3268 else
3269 {
3270 DrawTriangle(lineCoords[2], lineCoords[1], lineCoords[0], lineColor);
3271 DrawTriangle(lineCoords[5], lineCoords[4], lineCoords[3], lineColor);
3272 }
3273 }
3274 //--------------------------------------------------------------------
3275
3276 return value;
3277}
3278
3279// List Element control, returns element state
3280static bool GuiListElement(Rectangle bounds, const char *text, bool active, bool editMode)
3281{
3282 GuiControlState state = guiState;
3283
3284 if (!guiLocked && editMode) state = GUI_STATE_NORMAL;
3285
3286 // Update control
3287 //--------------------------------------------------------------------
3288 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3289 {
3290 Vector2 mousePoint = GetMousePosition();
3291
3292 if (CheckCollisionPointRec(mousePoint, bounds))
3293 {
3294 if (!active)
3295 {
3296 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = GUI_STATE_PRESSED;
3297 else state = GUI_STATE_FOCUSED;
3298 }
3299
3300 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) active = !active;
3301 }
3302 }
3303 //--------------------------------------------------------------------
3304
3305 // Draw control
3306 //--------------------------------------------------------------------
3307 // Draw element rectangle
3308 switch (state)
3309 {
3310 case GUI_STATE_NORMAL:
3311 {
3312 if (active)
3313 {
3314 DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha));
3315 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha));
3316 }
3317 } break;
3318 case GUI_STATE_FOCUSED:
3319 {
3320 DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha));
3321 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha));
3322 } break;
3323 case GUI_STATE_PRESSED:
3324 {
3325 DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha));
3326 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha));
3327 } break;
3328 case GUI_STATE_DISABLED:
3329 {
3330 if (active)
3331 {
3332 DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha));
3333 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), guiAlpha));
3334 }
3335 } break;
3336 default: break;
3337 }
3338
3339 // Draw text depending on state
3340 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));
3341 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));
3342 else GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + state*3)), guiAlpha));
3343 //--------------------------------------------------------------------
3344
3345 return active;
3346}
3347
3348// List View control
3349RAYGUIDEF bool GuiListView(Rectangle bounds, const char *text, int *active, int *scrollIndex, bool editMode)
3350{
3351 bool result = 0;
3352
3353 int count = 0;
3354 const char **textList = GuiTextSplit(text, &count, NULL);
3355
3356 result = GuiListViewEx(bounds, textList, count, NULL, active, NULL, scrollIndex, editMode);
3357
3358 return result;
3359}
3360
3361// List View control extended parameters
3362// NOTE: Elements could be disabled individually and focused element could be obtained:
3363// int *enabled defines an array with enabled elements inside the list
3364// int *focus returns focused element (may be not pressed)
3365RAYGUIDEF bool GuiListViewEx(Rectangle bounds, const char **text, int count, int *enabled, int *active, int *focus, int *scrollIndex, bool editMode)
3366{
3367 GuiControlState state = guiState;
3368 bool pressed = false;
3369
3370 int focusElement = -1;
3371 int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex;
3372 bool useScrollBar = true;
3373 bool pressedKey = false;
3374
3375 int visibleElements = bounds.height/(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING));
3376 if ((startIndex < 0) || (startIndex > count - visibleElements)) startIndex = 0;
3377 int endIndex = startIndex + visibleElements;
3378
3379 int auxActive = *active;
3380
3381 float barHeight = bounds.height;
3382 float minBarHeight = 10;
3383
3384 // Update control
3385 //--------------------------------------------------------------------
3386 // All the elements fit inside ListView and dont need scrollbar.
3387 if (visibleElements >= count)
3388 {
3389 useScrollBar = false;
3390 startIndex = 0;
3391 endIndex = count;
3392 }
3393
3394 // Calculate position X and width to draw each element.
3395 int posX = bounds.x + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING);
3396 int elementWidth = bounds.width - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
3397
3398 if (useScrollBar)
3399 {
3400 posX = GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE? posX + GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : posX;
3401 elementWidth = bounds.width - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) - 2*GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
3402 }
3403
3404 Rectangle scrollBarRect = { (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), (float)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) };
3405
3406 if (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_RIGHT_SIDE) scrollBarRect.x = posX + elementWidth + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING);
3407
3408 // Area without the scrollbar
3409 Rectangle viewArea = { (float)posX, (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)elementWidth, (float)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) };
3410
3411 if ((state != GUI_STATE_DISABLED) && !guiLocked) // && !guiLocked
3412 {
3413 Vector2 mousePoint = GetMousePosition();
3414
3415 if (editMode)
3416 {
3417 state = GUI_STATE_PRESSED;
3418
3419 // Change active with keys
3420 if (IsKeyPressed(KEY_UP))
3421 {
3422 if (auxActive > 0)
3423 {
3424 auxActive--;
3425 if ((useScrollBar) && (auxActive < startIndex)) startIndex--;
3426 }
3427
3428 pressedKey = true;
3429 }
3430 else if (IsKeyPressed(KEY_DOWN))
3431 {
3432 if (auxActive < count - 1)
3433 {
3434 auxActive++;
3435 if ((useScrollBar) && (auxActive >= endIndex)) startIndex++;
3436 }
3437
3438 pressedKey = true;
3439 }
3440
3441 if (useScrollBar)
3442 {
3443 endIndex = startIndex + visibleElements;
3444 if (CheckCollisionPointRec(mousePoint, viewArea))
3445 {
3446 int wheel = GetMouseWheelMove();
3447
3448 if (wheel < 0 && endIndex < count) startIndex -= wheel;
3449 else if (wheel > 0 && startIndex > 0) startIndex -= wheel;
3450 }
3451
3452 if (pressedKey)
3453 {
3454 pressedKey = false;
3455 if ((auxActive < startIndex) || (auxActive >= endIndex)) startIndex = auxActive;
3456 }
3457
3458 if (startIndex < 0) startIndex = 0;
3459 else if (startIndex > (count - (endIndex - startIndex)))
3460 {
3461 startIndex = count - (endIndex - startIndex);
3462 }
3463
3464 endIndex = startIndex + visibleElements;
3465
3466 if (endIndex > count) endIndex = count;
3467 }
3468 }
3469
3470 if (!editMode)
3471 {
3472 if (CheckCollisionPointRec(mousePoint, viewArea))
3473 {
3474 state = GUI_STATE_FOCUSED;
3475 if (IsMouseButtonPressed(0)) pressed = true;
3476
3477 startIndex -= GetMouseWheelMove();
3478
3479 if (startIndex < 0) startIndex = 0;
3480 else if (startIndex > (count - (endIndex - startIndex)))
3481 {
3482 startIndex = count - (endIndex - startIndex);
3483 }
3484
3485 pressed = true;
3486 }
3487 }
3488 else
3489 {
3490 if (!CheckCollisionPointRec(mousePoint, viewArea))
3491 {
3492 if (IsMouseButtonPressed(0) || (GetMouseWheelMove() != 0)) pressed = true;
3493 }
3494 }
3495
3496 // Get focused element
3497 for (int i = startIndex; i < endIndex; i++)
3498 {
3499 if (CheckCollisionPointRec(mousePoint, RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }))
3500 {
3501 focusElement = i;
3502 }
3503 }
3504 }
3505
3506 const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); // Save default slider size
3507
3508 // Calculate percentage of visible elements and apply same percentage to scrollbar
3509 if (useScrollBar)
3510 {
3511 float percentVisible = (endIndex - startIndex)*100/count;
3512 barHeight *= percentVisible/100;
3513
3514 if (barHeight < minBarHeight) barHeight = minBarHeight;
3515 else if (barHeight > bounds.height) barHeight = bounds.height;
3516
3517 GuiSetStyle(SCROLLBAR, SLIDER_SIZE, barHeight); // Change slider size
3518 }
3519 //--------------------------------------------------------------------
3520
3521 // Draw control
3522 //--------------------------------------------------------------------
3523 DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background
3524
3525 // Draw scrollBar
3526 if (useScrollBar)
3527 {
3528 const int scrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
3529 GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleElements); // Hack to make the spinner buttons work
3530
3531 int index = scrollIndex != NULL? *scrollIndex : startIndex;
3532 index = GuiScrollBar(scrollBarRect, index, 0, count - visibleElements);
3533
3534 GuiSetStyle(SCROLLBAR, SCROLL_SPEED, scrollSpeed); // Reset scroll speed to default
3535 GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider); // Reset slider size to default
3536
3537 // FIXME: Quick hack to make this thing work, think of a better way
3538 if (scrollIndex != NULL && CheckCollisionPointRec(GetMousePosition(), scrollBarRect) && IsMouseButtonDown(MOUSE_LEFT_BUTTON))
3539 {
3540 startIndex = index;
3541 if (startIndex < 0) startIndex = 0;
3542 if (startIndex > (count - (endIndex - startIndex)))
3543 {
3544 startIndex = count - (endIndex - startIndex);
3545 }
3546
3547 endIndex = startIndex + visibleElements;
3548
3549 if (endIndex > count) endIndex = count;
3550 }
3551 }
3552
3553 DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha));
3554
3555 // Draw ListView states
3556 switch (state)
3557 {
3558 case GUI_STATE_NORMAL:
3559 {
3560 for (int i = startIndex; i < endIndex; i++)
3561 {
3562 if ((enabled != NULL) && (enabled[i] == 0))
3563 {
3564 GuiDisable();
3565 GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false);
3566 GuiEnable();
3567 }
3568 else if (i == auxActive)
3569 {
3570 GuiDisable();
3571 GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false);
3572 GuiEnable();
3573 }
3574 else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false);
3575 }
3576 } break;
3577 case GUI_STATE_FOCUSED:
3578 {
3579 for (int i = startIndex; i < endIndex; i++)
3580 {
3581 if ((enabled != NULL) && (enabled[i] == 0))
3582 {
3583 GuiDisable();
3584 GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false);
3585 GuiEnable();
3586 }
3587 else if (i == auxActive) GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false);
3588 else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false);
3589 }
3590 } break;
3591 case GUI_STATE_PRESSED:
3592 {
3593 for (int i = startIndex; i < endIndex; i++)
3594 {
3595 if ((enabled != NULL) && (enabled[i] == 0))
3596 {
3597 GuiDisable();
3598 GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false);
3599 GuiEnable();
3600 }
3601 else if ((i == auxActive) && editMode)
3602 {
3603 if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, true) == false) auxActive = -1;
3604 }
3605 else
3606 {
3607 if (GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, true) == true) auxActive = i;
3608 }
3609 }
3610 } break;
3611 case GUI_STATE_DISABLED:
3612 {
3613 for (int i = startIndex; i < endIndex; i++)
3614 {
3615 if (i == auxActive) GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], true, false);
3616 else GuiListElement(RAYGUI_CLITERAL(Rectangle){ (float)posX, (float)bounds.y + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH) + (i - startIndex)*(GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) + GuiGetStyle(LISTVIEW, ELEMENTS_PADDING)), (float)elementWidth, (float)GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT) }, text[i], false, false);
3617 }
3618 } break;
3619 default: break;
3620 }
3621 //--------------------------------------------------------------------
3622
3623 if (scrollIndex != NULL) *scrollIndex = startIndex;
3624 if (focus != NULL) *focus = focusElement;
3625 *active = auxActive;
3626
3627 return pressed;
3628}
3629
3630// Color Panel control
3631RAYGUIDEF Color GuiColorPanelEx(Rectangle bounds, Color color, float hue)
3632{
3633 GuiControlState state = guiState;
3634 Vector2 pickerSelector = { 0 };
3635
3636 Vector3 vcolor = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
3637 Vector3 hsv = ConvertRGBtoHSV(vcolor);
3638
3639 pickerSelector.x = bounds.x + (float)hsv.y*bounds.width; // HSV: Saturation
3640 pickerSelector.y = bounds.y + (1.0f - (float)hsv.z)*bounds.height; // HSV: Value
3641
3642 Vector3 maxHue = { hue >= 0.0f ? hue : hsv.x, 1.0f, 1.0f };
3643 Vector3 rgbHue = ConvertHSVtoRGB(maxHue);
3644 Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x),
3645 (unsigned char)(255.0f*rgbHue.y),
3646 (unsigned char)(255.0f*rgbHue.z), 255 };
3647
3648 const Color colWhite = { 255, 255, 255, 255 };
3649 const Color colBlack = { 0, 0, 0, 255 };
3650
3651 // Update control
3652 //--------------------------------------------------------------------
3653 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3654 {
3655 Vector2 mousePoint = GetMousePosition();
3656
3657 if (CheckCollisionPointRec(mousePoint, bounds))
3658 {
3659 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
3660 {
3661 state = GUI_STATE_PRESSED;
3662 pickerSelector = mousePoint;
3663
3664 // Calculate color from picker
3665 Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
3666
3667 colorPick.x /= (float)bounds.width; // Get normalized value on x
3668 colorPick.y /= (float)bounds.height; // Get normalized value on y
3669
3670 hsv.y = colorPick.x;
3671 hsv.z = 1.0f - colorPick.y;
3672
3673 Vector3 rgb = ConvertHSVtoRGB(hsv);
3674
3675 // NOTE: Vector3ToColor() only available on raylib 1.8.1
3676 color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
3677 (unsigned char)(255.0f*rgb.y),
3678 (unsigned char)(255.0f*rgb.z),
3679 (unsigned char)(255.0f*(float)color.a/255.0f) };
3680
3681 }
3682 else state = GUI_STATE_FOCUSED;
3683 }
3684 }
3685 //--------------------------------------------------------------------
3686
3687 // Draw control
3688 //--------------------------------------------------------------------
3689 if (state != GUI_STATE_DISABLED)
3690 {
3691 DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha));
3692 DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0));
3693
3694 // Draw color picker: selector
3695 DrawRectangle(pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), Fade(colWhite, guiAlpha));
3696 }
3697 else
3698 {
3699 DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha));
3700 }
3701
3702 DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha));
3703 //--------------------------------------------------------------------
3704
3705 return color;
3706}
3707
3708RAYGUIDEF Color GuiColorPanel(Rectangle bounds, Color color)
3709{
3710 return GuiColorPanelEx(bounds, color, -1.0f);
3711}
3712
3713// Color Bar Alpha control
3714// NOTE: Returns alpha value normalized [0..1]
3715RAYGUIDEF float GuiColorBarAlpha(Rectangle bounds, float alpha)
3716{
3717 #define COLORBARALPHA_CHECKED_SIZE 10
3718
3719 GuiControlState state = guiState;
3720 Rectangle selector = { (float)bounds.x + alpha*bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.y - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2 };
3721
3722 // Update control
3723 //--------------------------------------------------------------------
3724 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3725 {
3726 Vector2 mousePoint = GetMousePosition();
3727
3728 if (CheckCollisionPointRec(mousePoint, bounds) ||
3729 CheckCollisionPointRec(mousePoint, selector))
3730 {
3731 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
3732 {
3733 state = GUI_STATE_PRESSED;
3734 selector.x = mousePoint.x - selector.width/2;
3735
3736 alpha = (mousePoint.x - bounds.x)/bounds.width;
3737 if (alpha <= 0.0f) alpha = 0.0f;
3738 if (alpha >= 1.0f) alpha = 1.0f;
3739 //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2;
3740 }
3741 else state = GUI_STATE_FOCUSED;
3742 }
3743 }
3744 //--------------------------------------------------------------------
3745
3746 // Draw control
3747 //--------------------------------------------------------------------
3748
3749 // Draw alpha bar: checked background
3750 if (state != GUI_STATE_DISABLED)
3751 {
3752 int checksX = bounds.width/COLORBARALPHA_CHECKED_SIZE;
3753 int checksY = bounds.height/COLORBARALPHA_CHECKED_SIZE;
3754
3755 for (int x = 0; x < checksX; x++)
3756 {
3757 for (int y = 0; y < checksY; y++)
3758 {
3759 DrawRectangle(bounds.x + x*COLORBARALPHA_CHECKED_SIZE,
3760 bounds.y + y*COLORBARALPHA_CHECKED_SIZE,
3761 COLORBARALPHA_CHECKED_SIZE, COLORBARALPHA_CHECKED_SIZE,
3762 ((x + y)%2)? Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) :
3763 Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha));
3764 }
3765 }
3766
3767 DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha));
3768 }
3769 else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
3770
3771 DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha));
3772
3773 // Draw alpha bar: selector
3774 DrawRectangleRec(selector, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha));
3775 //--------------------------------------------------------------------
3776
3777 return alpha;
3778}
3779
3780// Color Bar Hue control
3781// NOTE: Returns hue value normalized [0..1]
3782RAYGUIDEF float GuiColorBarHue(Rectangle bounds, float hue)
3783{
3784 GuiControlState state = guiState;
3785 Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.y + hue/360.0f*bounds.height - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (float)bounds.width + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)*2, (float)GuiGetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT) };
3786
3787 // Update control
3788 //--------------------------------------------------------------------
3789 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3790 {
3791 Vector2 mousePoint = GetMousePosition();
3792
3793 if (CheckCollisionPointRec(mousePoint, bounds) ||
3794 CheckCollisionPointRec(mousePoint, selector))
3795 {
3796 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
3797 {
3798 state = GUI_STATE_PRESSED;
3799 selector.y = mousePoint.y - selector.height/2;
3800
3801 hue = (mousePoint.y - bounds.y)*360/bounds.height;
3802 if (hue <= 0.0f) hue = 0.0f;
3803 if (hue >= 359.0f) hue = 359.0f;
3804
3805 }
3806 else state = GUI_STATE_FOCUSED;
3807
3808 /*if (IsKeyDown(KEY_UP))
3809 {
3810 hue -= 2.0f;
3811 if (hue <= 0.0f) hue = 0.0f;
3812 }
3813 else if (IsKeyDown(KEY_DOWN))
3814 {
3815 hue += 2.0f;
3816 if (hue >= 360.0f) hue = 360.0f;
3817 }*/
3818 }
3819 }
3820 //--------------------------------------------------------------------
3821
3822 // Draw control
3823 //--------------------------------------------------------------------
3824 if (state != GUI_STATE_DISABLED)
3825 {
3826 // Draw hue bar:color bars
3827 DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha));
3828 DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + (int)bounds.height/6 + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 255,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha));
3829 DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 2*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,0,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha));
3830 DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 3*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,255,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha));
3831 DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 4*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6, Fade(RAYGUI_CLITERAL(Color){ 0,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha));
3832 DrawRectangleGradientV(bounds.x + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.y + 5*((int)bounds.height/6) + GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING)/2, bounds.width - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), (int)bounds.height/6 - GuiGetStyle(COLORPICKER, BAR_SELECTOR_PADDING), Fade(RAYGUI_CLITERAL(Color){ 255,0,255,255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255,0,0,255 }, guiAlpha));
3833 }
3834 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));
3835
3836 DrawRectangleLinesEx(bounds, 1, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha));
3837
3838 // Draw hue bar: selector
3839 DrawRectangleRec(selector, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha));
3840 //--------------------------------------------------------------------
3841
3842 return hue;
3843}
3844
3845// TODO: Color GuiColorBarSat() [WHITE->color]
3846// TODO: Color GuiColorBarValue() [BLACK->color], HSV / HSL
3847// TODO: float GuiColorBarLuminance() [BLACK->WHITE]
3848
3849// Color Picker control
3850// NOTE: It's divided in multiple controls:
3851// Color GuiColorPanel() - Color select panel
3852// float GuiColorBarAlpha(Rectangle bounds, float alpha)
3853// float GuiColorBarHue(Rectangle bounds, float value)
3854// NOTE: bounds define GuiColorPanel() size
3855RAYGUIDEF Color GuiColorPicker(Rectangle bounds, Color color)
3856{
3857 color = GuiColorPanel(bounds, color);
3858
3859 Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, BAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, BAR_WIDTH), (float)bounds.height };
3860 //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
3861
3862 Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ color.r/255.0f, color.g/255.0f, color.b/255.0f });
3863 hsv.x = GuiColorBarHue(boundsHue, hsv.x);
3864 //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f);
3865 Vector3 rgb = ConvertHSVtoRGB(hsv);
3866 color = RAYGUI_CLITERAL(Color){ (unsigned char)(rgb.x*255.0f), (unsigned char)(rgb.y*255.0f), (unsigned char)(rgb.z*255.0f), color.a };
3867
3868 return color;
3869}
3870
3871// Message Box control
3872RAYGUIDEF int GuiMessageBox(Rectangle bounds, const char *windowTitle, const char *message, const char *buttons)
3873{
3874 #define MESSAGEBOX_BUTTON_HEIGHT 24
3875 #define MESSAGEBOX_BUTTON_PADDING 10
3876
3877 int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button
3878
3879 int buttonsCount = 0;
3880 const char **buttonsText = GuiTextSplit(buttons, &buttonsCount, NULL);
3881
3882 Vector2 textSize = MeasureTextEx(guiFont, message, GuiGetStyle(DEFAULT, TEXT_SIZE), 1);
3883
3884 Rectangle textBounds = { 0 };
3885 textBounds.x = bounds.x + bounds.width/2 - textSize.x/2;
3886 textBounds.y = bounds.y + WINDOW_STATUSBAR_HEIGHT + (bounds.height - WINDOW_STATUSBAR_HEIGHT)/4 - textSize.y/2;
3887 textBounds.width = textSize.x;
3888 textBounds.height = textSize.y;
3889
3890 Rectangle buttonBounds = { 0 };
3891 buttonBounds.x = bounds.x + MESSAGEBOX_BUTTON_PADDING;
3892 buttonBounds.y = bounds.y + bounds.height/2 + bounds.height/4 - MESSAGEBOX_BUTTON_HEIGHT/2;
3893 buttonBounds.width = (bounds.width - MESSAGEBOX_BUTTON_PADDING*(buttonsCount + 1))/buttonsCount;
3894 buttonBounds.height = MESSAGEBOX_BUTTON_HEIGHT;
3895
3896 // Draw control
3897 //--------------------------------------------------------------------
3898 if (GuiWindowBox(bounds, windowTitle)) clicked = 0;
3899
3900 int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
3901 GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
3902 GuiLabel(textBounds, message);
3903 GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
3904
3905 prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
3906 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
3907
3908 for (int i = 0; i < buttonsCount; i++)
3909 {
3910 if (GuiButton(buttonBounds, buttonsText[i])) clicked = i + 1;
3911 buttonBounds.x += (buttonBounds.width + MESSAGEBOX_BUTTON_PADDING);
3912 }
3913
3914 GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment);
3915 //--------------------------------------------------------------------
3916
3917 return clicked;
3918}
3919
3920// Text Input Box control, ask for text
3921RAYGUIDEF int GuiTextInputBox(Rectangle bounds, const char *windowTitle, const char *message, char *text, const char *buttons)
3922{
3923 int btnIndex = -1;
3924
3925 // TODO: GuiTextInputBox()
3926
3927 return btnIndex;
3928}
3929
3930// Grid control
3931// NOTE: Returns grid mouse-hover selected cell
3932// About drawing lines at subpixel spacing, simple put, not easy solution:
3933// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
3934RAYGUIDEF Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs)
3935{
3936 #define GRID_COLOR_ALPHA 0.15f // Grid lines alpha amount
3937
3938 GuiControlState state = guiState;
3939 Vector2 mousePoint = GetMousePosition();
3940 Vector2 currentCell = { -1, -1 };
3941
3942 int linesV = ((int)(bounds.width/spacing) + 1)*subdivs;
3943 int linesH = ((int)(bounds.height/spacing) + 1)*subdivs;
3944
3945 // Update control
3946 //--------------------------------------------------------------------
3947 if ((state != GUI_STATE_DISABLED) && !guiLocked)
3948 {
3949 if (CheckCollisionPointRec(mousePoint, bounds))
3950 {
3951 currentCell.x = (int)((mousePoint.x - bounds.x)/spacing);
3952 currentCell.y = (int)((mousePoint.y - bounds.y)/spacing);
3953 }
3954 }
3955 //--------------------------------------------------------------------
3956
3957 // Draw control
3958 //--------------------------------------------------------------------
3959 switch (state)
3960 {
3961 case GUI_STATE_NORMAL:
3962 {
3963 // Draw vertical grid lines
3964 for (int i = 0; i < linesV; i++)
3965 {
3966 DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x + spacing*i, bounds.y, 1, bounds.height }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA));
3967 }
3968
3969 // Draw horizontal grid lines
3970 for (int i = 0; i < linesH; i++)
3971 {
3972 DrawRectangleRec(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + spacing*i, bounds.width, 1 }, ((i%subdivs) == 0)? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA));
3973 }
3974
3975 } break;
3976 default: break;
3977 }
3978
3979 return currentCell;
3980}
3981
3982//----------------------------------------------------------------------------------
3983// Styles loading functions
3984//----------------------------------------------------------------------------------
3985
3986// Load raygui style file (.rgs)
3987RAYGUIDEF void GuiLoadStyle(const char *fileName)
3988{
3989 bool tryBinary = false;
3990
3991 // Try reading the files as text file first
3992 FILE *rgsFile = fopen(fileName, "rt");
3993
3994 if (rgsFile != NULL)
3995 {
3996 char buffer[256] = { 0 };
3997 fgets(buffer, 256, rgsFile);
3998
3999 if (buffer[0] == '#')
4000 {
4001 int controlId = 0;
4002 int propertyId = 0;
4003 int propertyValue = 0;
4004
4005 while (!feof(rgsFile))
4006 {
4007 switch (buffer[0])
4008 {
4009 case 'p':
4010 {
4011 sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue);
4012
4013 if (controlId == 0) // DEFAULT control
4014 {
4015 // If a DEFAULT property is loaded, it is propagated to all controls,
4016 // NOTE: All DEFAULT properties should be defined first in the file
4017 GuiSetStyle(0, propertyId, propertyValue);
4018
4019 if (propertyId < NUM_PROPS_DEFAULT) for (int i = 1; i < NUM_CONTROLS; i++) GuiSetStyle(i, propertyId, propertyValue);
4020 }
4021 else GuiSetStyle(controlId, propertyId, propertyValue);
4022
4023 } break;
4024 case 'f':
4025 {
4026 int fontSize = 0;
4027 int fontSpacing = 0;
4028 char fontFileName[256] = { 0 };
4029 sscanf(buffer, "f %d %d %[^\n]s", &fontSize, &fontSpacing, fontFileName);
4030
4031 Font font = LoadFontEx(FormatText("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0);
4032
4033 if ((font.texture.id > 0) && (font.charsCount > 0))
4034 {
4035 GuiFont(font);
4036 GuiSetStyle(DEFAULT, TEXT_SIZE, fontSize);
4037 GuiSetStyle(DEFAULT, TEXT_SPACING, fontSpacing);
4038 }
4039 } break;
4040 default: break;
4041 }
4042
4043 fgets(buffer, 256, rgsFile);
4044 }
4045 }
4046 else tryBinary = true;
4047
4048 fclose(rgsFile);
4049 }
4050 else return;
4051
4052 if (tryBinary)
4053 {
4054 rgsFile = fopen(fileName, "rb");
4055
4056 if (rgsFile == NULL) return;
4057
4058 char signature[5] = "";
4059 short version = 0;
4060 short reserved = 0;
4061 int propertiesCount = 0;
4062
4063 fread(signature, 1, 4, rgsFile);
4064 fread(&version, 1, sizeof(short), rgsFile);
4065 fread(&reserved, 1, sizeof(short), rgsFile);
4066 fread(&propertiesCount, 1, sizeof(int), rgsFile);
4067
4068 if ((signature[0] == 'r') &&
4069 (signature[1] == 'G') &&
4070 (signature[2] == 'S') &&
4071 (signature[3] == ' '))
4072 {
4073 short controlId = 0;
4074 short propertyId = 0;
4075 int propertyValue = 0;
4076
4077 for (int i = 0; i < propertiesCount; i++)
4078 {
4079 fread(&controlId, 1, sizeof(short), rgsFile);
4080 fread(&propertyId, 1, sizeof(short), rgsFile);
4081 fread(&propertyValue, 1, sizeof(int), rgsFile);
4082
4083 if (controlId == 0) // DEFAULT control
4084 {
4085 // If a DEFAULT property is loaded, it is propagated to all controls
4086 // NOTE: All DEFAULT properties should be defined first in the file
4087 GuiSetStyle(0, (int)propertyId, propertyValue);
4088
4089 if (propertyId < NUM_PROPS_DEFAULT) for (int i = 1; i < NUM_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue);
4090 }
4091 else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
4092 }
4093
4094 // Font loading is highly dependant on raylib API to load font data and image
4095 // TODO: Find some mechanism to support it in standalone mode
4096#if !defined(RAYGUI_STANDALONE)
4097 // Load custom font if available
4098 int fontDataSize = 0;
4099 fread(&fontDataSize, 1, sizeof(int), rgsFile);
4100
4101 if (fontDataSize > 0)
4102 {
4103 Font font = { 0 };
4104 int fontType = 0; // 0-Normal, 1-SDF
4105 Rectangle whiteRec = { 0 };
4106
4107 fread(&font.baseSize, 1, sizeof(int), rgsFile);
4108 fread(&font.charsCount, 1, sizeof(int), rgsFile);
4109 fread(&fontType, 1, sizeof(int), rgsFile);
4110
4111 // Load font white rectangle
4112 fread(&whiteRec, 1, sizeof(Rectangle), rgsFile);
4113
4114 // Load font image parameters
4115 int fontImageSize = 0;
4116 fread(&fontImageSize, 1, sizeof(int), rgsFile);
4117
4118 if (fontImageSize > 0)
4119 {
4120 Image imFont = { 0 };
4121 imFont.mipmaps = 1;
4122 fread(&imFont.width, 1, sizeof(int), rgsFile);
4123 fread(&imFont.height, 1, sizeof(int), rgsFile);
4124 fread(&imFont.format, 1, sizeof(int), rgsFile);
4125
4126 imFont.data = (unsigned char *)malloc(fontImageSize);
4127 fread(imFont.data, 1, fontImageSize, rgsFile);
4128
4129 font.texture = LoadTextureFromImage(imFont);
4130
4131 UnloadImage(imFont);
4132 }
4133
4134 // Load font chars data
4135 font.chars = (CharInfo *)calloc(font.charsCount, sizeof(CharInfo));
4136 for (int i = 0; i < font.charsCount; i++)
4137 {
4138 fread(&font.recs[i], 1, sizeof(Rectangle), rgsFile);
4139 fread(&font.chars[i].value, 1, sizeof(int), rgsFile);
4140 fread(&font.chars[i].offsetX, 1, sizeof(int), rgsFile);
4141 fread(&font.chars[i].offsetY, 1, sizeof(int), rgsFile);
4142 fread(&font.chars[i].advanceX, 1, sizeof(int), rgsFile);
4143 }
4144
4145 GuiFont(font);
4146
4147 // Set font texture source rectangle to be used as white texture to draw shapes
4148 // NOTE: This way, all gui can be draw using a single draw call
4149 if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec);
4150 }
4151#endif
4152 }
4153
4154 fclose(rgsFile);
4155 }
4156}
4157
4158// Load style from a palette values array
4159RAYGUIDEF void GuiLoadStyleProps(const int *props, int count)
4160{
4161 int completeSets = count/(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED);
4162 int uncompleteSetProps = count%(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED);
4163
4164 // Load style palette values from array (complete property sets)
4165 for (int i = 0; i < completeSets; i++)
4166 {
4167 for (int j = 0; j < (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED); j++) GuiSetStyle(i, j, props[i]);
4168 }
4169
4170 // Load style palette values from array (uncomplete property set)
4171 for (int k = 0; k < uncompleteSetProps; k++) GuiSetStyle(completeSets, k, props[completeSets*(NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + k]);
4172}
4173
4174// Load style default over global style
4175RAYGUIDEF void GuiLoadStyleDefault(void)
4176{
4177 // We set this variable first to avoid cyclic function calls
4178 // when calling GuiSetStyle() and GuiGetStyle()
4179 guiStyleLoaded = true;
4180
4181 // Initialize default LIGHT style property values
4182 GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff);
4183 GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff);
4184 GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff);
4185 GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff);
4186 GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff);
4187 GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff);
4188 GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff);
4189 GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff);
4190 GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff);
4191 GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff);
4192 GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff);
4193 GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff);
4194 GuiSetStyle(DEFAULT, BORDER_WIDTH, 1);
4195 GuiSetStyle(DEFAULT, INNER_PADDING, 1);
4196 GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
4197
4198 // Populate all controls with default style
4199 for (int i = 1; i < NUM_CONTROLS; i++)
4200 {
4201 for (int j = 0; j < NUM_PROPS_DEFAULT; j++) GuiSetStyle(i, j, GuiGetStyle(DEFAULT, j));
4202 }
4203
4204 guiFont = GetFontDefault(); // Initialize default font
4205
4206 // Initialize extended property values
4207 // NOTE: By default, extended property values are initialized to 0
4208 GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
4209 GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
4210 GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property
4211 GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property
4212
4213 GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
4214 GuiSetStyle(BUTTON, BORDER_WIDTH, 2);
4215 GuiSetStyle(BUTTON, INNER_PADDING, 4);
4216 GuiSetStyle(TOGGLE, GROUP_PADDING, 2);
4217 GuiSetStyle(SLIDER, SLIDER_WIDTH, 15);
4218 GuiSetStyle(SLIDER, TEXT_PADDING, 5);
4219 GuiSetStyle(CHECKBOX, CHECK_TEXT_PADDING, 5);
4220 GuiSetStyle(COMBOBOX, SELECTOR_WIDTH, 30);
4221 GuiSetStyle(COMBOBOX, SELECTOR_PADDING, 2);
4222 GuiSetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING, 16);
4223 GuiSetStyle(TEXTBOX, INNER_PADDING, 4);
4224 GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_LEFT);
4225 GuiSetStyle(TEXTBOX, MULTILINE_PADDING, 5);
4226 GuiSetStyle(TEXTBOX, COLOR_SELECTED_FG, 0xf0fffeff);
4227 GuiSetStyle(TEXTBOX, COLOR_SELECTED_BG, 0x839affe0);
4228 GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
4229 GuiSetStyle(SPINNER, SELECT_BUTTON_WIDTH, 20);
4230 GuiSetStyle(SPINNER, SELECT_BUTTON_PADDING, 2);
4231 GuiSetStyle(SPINNER, SELECT_BUTTON_BORDER_WIDTH, 1);
4232 GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
4233 GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
4234 GuiSetStyle(SCROLLBAR, INNER_PADDING, 0);
4235 GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
4236 GuiSetStyle(SCROLLBAR, SLIDER_PADDING, 0);
4237 GuiSetStyle(SCROLLBAR, SLIDER_SIZE, 16);
4238 GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 10);
4239 GuiSetStyle(LISTVIEW, ELEMENTS_HEIGHT, 0x1e);
4240 GuiSetStyle(LISTVIEW, ELEMENTS_PADDING, 2);
4241 GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 10);
4242 GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
4243 GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 6);
4244 GuiSetStyle(COLORPICKER, BAR_WIDTH, 0x14);
4245 GuiSetStyle(COLORPICKER, BAR_PADDING, 0xa);
4246 GuiSetStyle(COLORPICKER, BAR_SELECTOR_HEIGHT, 6);
4247 GuiSetStyle(COLORPICKER, BAR_SELECTOR_PADDING, 2);
4248}
4249
4250// Updates controls style with default values
4251RAYGUIDEF void GuiUpdateStyleComplete(void)
4252{
4253 // Populate all controls with default style
4254 // NOTE: Extended style properties are ignored
4255 for (int i = 1; i < NUM_CONTROLS; i++)
4256 {
4257 for (int j = 0; j < NUM_PROPS_DEFAULT; j++) GuiSetStyle(i, j, GuiGetStyle(DEFAULT, j));
4258 }
4259}
4260
4261// Get text with icon id prepended
4262// NOTE: Useful to add icons by name id (enum) instead of
4263// a number that can change between ricon versions
4264RAYGUIDEF const char *GuiIconText(int iconId, const char *text)
4265{
4266 static char buffer[1024] = { 0 };
4267 memset(buffer, 0, 1024);
4268
4269 sprintf(buffer, "#%03i#", iconId);
4270
4271 if (text != NULL)
4272 {
4273 for (int i = 5; i < 1024; i++)
4274 {
4275 buffer[i] = text[i - 5];
4276 if (text[i - 5] == '\0') break;
4277 }
4278 }
4279
4280 return buffer;
4281}
4282
4283//----------------------------------------------------------------------------------
4284// Module specific Functions Definition
4285//----------------------------------------------------------------------------------
4286
4287// Split controls text into multiple strings
4288// Also check for multiple columns (required by GuiToggleGroup())
4289static const char **GuiTextSplit(const char *text, int *count, int *textRow)
4290{
4291 // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
4292 // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
4293 // all used memory is static... it has some limitations:
4294 // 1. Maximum number of possible split strings is set by MAX_SUBSTRINGS_COUNT
4295 // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH
4296
4297 #define MAX_TEXT_BUFFER_LENGTH 1024
4298 #define MAX_SUBSTRINGS_COUNT 64
4299
4300 static const char *result[MAX_SUBSTRINGS_COUNT] = { NULL };
4301 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
4302 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
4303
4304 result[0] = buffer;
4305 int counter = 1;
4306
4307 if (textRow != NULL) textRow[0] = 0;
4308
4309 // Count how many substrings we have on text and point to every one
4310 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
4311 {
4312 buffer[i] = text[i];
4313 if (buffer[i] == '\0') break;
4314 else if ((buffer[i] == ';') || (buffer[i] == '\n'))
4315 {
4316 result[counter] = buffer + i + 1;
4317
4318 if (textRow != NULL)
4319 {
4320 if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1;
4321 else textRow[counter] = textRow[counter - 1];
4322 }
4323
4324 buffer[i] = '\0'; // Set an end of string at this point
4325
4326 counter++;
4327 if (counter == MAX_SUBSTRINGS_COUNT) break;
4328 }
4329 }
4330
4331 *count = counter;
4332
4333 return result;
4334}
4335
4336// Convert color data from RGB to HSV
4337// NOTE: Color data should be passed normalized
4338static Vector3 ConvertRGBtoHSV(Vector3 rgb)
4339{
4340 Vector3 hsv = { 0.0f };
4341 float min = 0.0f;
4342 float max = 0.0f;
4343 float delta = 0.0f;
4344
4345 min = (rgb.x < rgb.y)? rgb.x : rgb.y;
4346 min = (min < rgb.z)? min : rgb.z;
4347
4348 max = (rgb.x > rgb.y)? rgb.x : rgb.y;
4349 max = (max > rgb.z)? max : rgb.z;
4350
4351 hsv.z = max; // Value
4352 delta = max - min;
4353
4354 if (delta < 0.00001f)
4355 {
4356 hsv.y = 0.0f;
4357 hsv.x = 0.0f; // Undefined, maybe NAN?
4358 return hsv;
4359 }
4360
4361 if (max > 0.0f)
4362 {
4363 // NOTE: If max is 0, this divide would cause a crash
4364 hsv.y = (delta/max); // Saturation
4365 }
4366 else
4367 {
4368 // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
4369 hsv.y = 0.0f;
4370 hsv.x = 0.0f; // Undefined, maybe NAN?
4371 return hsv;
4372 }
4373
4374 // NOTE: Comparing float values could not work properly
4375 if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta
4376 else
4377 {
4378 if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow
4379 else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan
4380 }
4381
4382 hsv.x *= 60.0f; // Convert to degrees
4383
4384 if (hsv.x < 0.0f) hsv.x += 360.0f;
4385
4386 return hsv;
4387}
4388
4389// Convert color data from HSV to RGB
4390// NOTE: Color data should be passed normalized
4391static Vector3 ConvertHSVtoRGB(Vector3 hsv)
4392{
4393 Vector3 rgb = { 0.0f };
4394 float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f;
4395 long i = 0;
4396
4397 // NOTE: Comparing float values could not work properly
4398 if (hsv.y <= 0.0f)
4399 {
4400 rgb.x = hsv.z;
4401 rgb.y = hsv.z;
4402 rgb.z = hsv.z;
4403 return rgb;
4404 }
4405
4406 hh = hsv.x;
4407 if (hh >= 360.0f) hh = 0.0f;
4408 hh /= 60.0f;
4409
4410 i = (long)hh;
4411 ff = hh - i;
4412 p = hsv.z*(1.0f - hsv.y);
4413 q = hsv.z*(1.0f - (hsv.y*ff));
4414 t = hsv.z*(1.0f - (hsv.y*(1.0f - ff)));
4415
4416 switch (i)
4417 {
4418 case 0:
4419 {
4420 rgb.x = hsv.z;
4421 rgb.y = t;
4422 rgb.z = p;
4423 } break;
4424 case 1:
4425 {
4426 rgb.x = q;
4427 rgb.y = hsv.z;
4428 rgb.z = p;
4429 } break;
4430 case 2:
4431 {
4432 rgb.x = p;
4433 rgb.y = hsv.z;
4434 rgb.z = t;
4435 } break;
4436 case 3:
4437 {
4438 rgb.x = p;
4439 rgb.y = q;
4440 rgb.z = hsv.z;
4441 } break;
4442 case 4:
4443 {
4444 rgb.x = t;
4445 rgb.y = p;
4446 rgb.z = hsv.z;
4447 } break;
4448 case 5:
4449 default:
4450 {
4451 rgb.x = hsv.z;
4452 rgb.y = p;
4453 rgb.z = q;
4454 } break;
4455 }
4456
4457 return rgb;
4458}
4459
4460#if defined(RAYGUI_STANDALONE)
4461// Returns a Color struct from hexadecimal value
4462static Color GetColor(int hexValue)
4463{
4464 Color color;
4465
4466 color.r = (unsigned char)(hexValue >> 24) & 0xFF;
4467 color.g = (unsigned char)(hexValue >> 16) & 0xFF;
4468 color.b = (unsigned char)(hexValue >> 8) & 0xFF;
4469 color.a = (unsigned char)hexValue & 0xFF;
4470
4471 return color;
4472}
4473
4474// Returns hexadecimal value for a Color
4475static int ColorToInt(Color color)
4476{
4477 return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
4478}
4479
4480// Check if point is inside rectangle
4481static bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
4482{
4483 bool collision = false;
4484
4485 if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) &&
4486 (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true;
4487
4488 return collision;
4489}
4490
4491// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
4492static Color Fade(Color color, float alpha)
4493{
4494 if (alpha < 0.0f) alpha = 0.0f;
4495 else if (alpha > 1.0f) alpha = 1.0f;
4496
4497 return RAYGUI_CLITERAL(Color){ color.r, color.g, color.b, (unsigned char)(255.0f*alpha) };
4498}
4499
4500// Formatting of text with variables to 'embed'
4501static const char *TextFormat(const char *text, ...)
4502{
4503 #define MAX_FORMATTEXT_LENGTH 64
4504
4505 static char buffer[MAX_FORMATTEXT_LENGTH];
4506
4507 va_list args;
4508 va_start(args, text);
4509 vsprintf(buffer, text, args);
4510 va_end(args);
4511
4512 return buffer;
4513}
4514
4515// Draw rectangle filled with color
4516static void DrawRectangleRec(Rectangle rec, Color color)
4517{
4518 DrawRectangle(rec.x, rec.y, rec.width, rec.height, color);
4519}
4520
4521// Draw rectangle border lines with color
4522static void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color)
4523{
4524 DrawRectangle(rec.x, rec.y, rec.width, lineThick, color);
4525 DrawRectangle(rec.x, rec.y + lineThick, lineThick, rec.height - 2*lineThick, color);
4526 DrawRectangle(rec.x + rec.width - lineThick, rec.y + lineThick, lineThick, rec.height - 2*lineThick, color);
4527 DrawRectangle(rec.x, rec.y + rec.height - lineThick, rec.width, lineThick, color);
4528}
4529
4530// Draw rectangle with vertical gradient fill color
4531// NOTE: This function is only used by GuiColorPicker()
4532static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2)
4533{
4534 Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height };
4535 DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
4536}
4537
4538#endif // RAYGUI_STANDALONE
4539
4540#endif // RAYGUI_IMPLEMENTATION