1 module draygui.raygui;
2 
3 /**
4 *   raygui v2.5 - A simple and easy-to-use immedite-mode-gui library
5 *
6 *   raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also possible
7 *   to be used as a standalone library, as long as input and drawing functions are provided.
8 *
9 *   Controls provided:
10 *
11 *   # Container/separators Controls
12 *       - WindowBox
13 *       - GroupBox
14 *       - Line
15 *       - Panel
16 *
17 *   # Basic Controls
18 *       - Label
19 *       - Button
20 *       - LabelButton   --> Label
21 *       - ImageButton   --> Button
22 *       - ImageButtonEx --> Button
23 *       - Toggle
24 *       - ToggleGroup   --> Toggle
25 *       - CheckBox
26 *       - ComboBox
27 *       - DropdownBox
28 *       - TextBox
29 *       - TextBoxMulti
30 *       - ValueBox      --> TextBox
31 *       - Spinner       --> Button, ValueBox
32 *       - Slider
33 *       - SliderBar     --> Slider
34 *       - ProgressBar
35 *       - StatusBar
36 *       - ScrollBar
37 *       - ScrollPanel
38 *       - DummyRec
39 *       - Grid
40 *
41 *   # Advance Controls
42 *       - ListView      --> ListElement
43 *       - ColorPicker   --> ColorPanel, ColorBarHue
44 *       - MessageBox    --> Label, Button
45 *       - TextInputBox  --> Label, TextBox, Button
46 *
47 */
48 
49 import raylib;
50 
51 ///Current Raygui version.
52 enum RAYGUI_VERSION = "2.5-dev";
53 //Following https://github.com/raysan5/raygui/blob/75769bdd6bba6e5ac335ef596dbef9fe8a7e41d0/src/raygui.h
54 
55 // We are building raygui as a Win32 shared library (.dll).
56 // We are using raygui as a Win32 shared library (.dll)
57 // Functions visible from other files (no name mangling of functions in C++) // Functions visible from other files
58 // Functions just visible to module including this file
59 // Required for: atoi()
60 
61 /// Vertical alignment for pixel perfect
62 auto VALIGN_OFFSET(T)(auto ref T h) {
63    return cast(int)h % 2;
64 }
65 
66 /// Text edit controls cursor blink timming
67 enum TEXTEDIT_CURSOR_BLINK_FRAMES = 20;
68 /// Number of standard controls
69 enum NUM_CONTROLS = 16;
70 /// Number of standard properties
71 enum NUM_PROPS_DEFAULT = 16;
72 /// Number of extended properties
73 enum NUM_PROPS_EXTENDED = 8;
74 
75 // Types and Structures Definition
76 // NOTE: Some types are required for RAYGUI_STANDALONE usage
77 // Boolean type
78 // Vector2 type
79 // Vector3 type
80 // Color type, RGBA (32bit)
81 // Rectangle type
82 // Texture2D type
83 // Font type
84 // Gui text box state data
85 // Cursor position in text
86 // Text start position (from where we begin drawing the text)
87 // Text start index (index inside the text of `start` always in sync)
88 // Marks position of cursor when selection has started
89 
90 /// Gui control state
91 enum GuiControlState {
92    GUI_STATE_NORMAL = 0,
93    GUI_STATE_FOCUSED = 1,
94    GUI_STATE_PRESSED = 2,
95    GUI_STATE_DISABLED = 3
96 }
97 
98 /// Gui control text alignment
99 enum GuiTextAlignment {
100    GUI_TEXT_ALIGN_LEFT = 0,
101    GUI_TEXT_ALIGN_CENTER = 1,
102    GUI_TEXT_ALIGN_RIGHT = 2
103 }
104 
105 /// Gui controls
106 enum GuiControl {
107    DEFAULT = 0,
108    LABEL = 1, // LABELBUTTON
109    BUTTON = 2, // IMAGEBUTTON
110    TOGGLE = 3, // TOGGLEGROUP
111    SLIDER = 4, // SLIDERBAR
112    PROGRESSBAR = 5,
113    CHECKBOX = 6,
114    COMBOBOX = 7,
115    DROPDOWNBOX = 8,
116    TEXTBOX = 9, // TEXTBOXMULTI
117    VALUEBOX = 10,
118    SPINNER = 11,
119    LISTVIEW = 12,
120    COLORPICKER = 13,
121    SCROLLBAR = 14,
122    RESERVED = 15
123 }
124 
125 /// Gui base properties for every control
126 enum GuiControlProperty {
127    BORDER_COLOR_NORMAL = 0,
128    BASE_COLOR_NORMAL = 1,
129    TEXT_COLOR_NORMAL = 2,
130    BORDER_COLOR_FOCUSED = 3,
131    BASE_COLOR_FOCUSED = 4,
132    TEXT_COLOR_FOCUSED = 5,
133    BORDER_COLOR_PRESSED = 6,
134    BASE_COLOR_PRESSED = 7,
135    TEXT_COLOR_PRESSED = 8,
136    BORDER_COLOR_DISABLED = 9,
137    BASE_COLOR_DISABLED = 10,
138    TEXT_COLOR_DISABLED = 11,
139    BORDER_WIDTH = 12,
140    INNER_PADDING = 13,
141    TEXT_ALIGNMENT = 14,
142    RESERVED02 = 15
143 }
144 
145 // Gui extended properties depend on control
146 // NOTE: We reserve a fixed size of additional properties per control
147 /// DEFAULT properties
148 enum GuiDefaultProperty {
149    TEXT_SIZE = 16,
150    TEXT_SPACING = 17,
151    LINE_COLOR = 18,
152    BACKGROUND_COLOR = 19
153 }
154 
155 /// Toggle / ToggleGroup
156 enum GuiToggleProperty {
157    GROUP_PADDING = 16
158 }
159 
160 /// Slider / SliderBar
161 enum GuiSliderProperty {
162    SLIDER_WIDTH = 16,
163    TEXT_PADDING = 17
164 }
165 
166 /// CheckBox
167 enum GuiCheckBoxProperty {
168    CHECK_TEXT_PADDING = 16
169 }
170 
171 /// ComboBox
172 enum GuiComboBoxProperty {
173    SELECTOR_WIDTH = 16,
174    SELECTOR_PADDING = 17
175 }
176 
177 /// DropdownBox
178 enum GuiDropdownBoxProperty {
179    ARROW_RIGHT_PADDING = 16
180 }
181 
182 /// TextBox / TextBoxMulti / ValueBox / Spinner
183 enum GuiTextBoxProperty {
184    MULTILINE_PADDING = 16,
185    COLOR_SELECTED_FG = 17,
186    COLOR_SELECTED_BG = 18
187 }
188 
189 enum GuiSpinnerProperty {
190    SELECT_BUTTON_WIDTH = 16,
191    SELECT_BUTTON_PADDING = 17,
192    SELECT_BUTTON_BORDER_WIDTH = 18
193 }
194 
195 /// ScrollBar
196 enum GuiScrollBarProperty {
197    ARROWS_SIZE = 16,
198    SLIDER_PADDING = 17,
199    SLIDER_SIZE = 18,
200    SCROLL_SPEED = 19,
201    SHOW_SPINNER_BUTTONS = 20
202 }
203 
204 /// ScrollBar side
205 enum GuiScrollBarSide {
206    SCROLLBAR_LEFT_SIDE = 0,
207    SCROLLBAR_RIGHT_SIDE = 1
208 }
209 
210 /// ListView
211 enum GuiListViewProperty {
212    ELEMENTS_HEIGHT = 16,
213    ELEMENTS_PADDING = 17,
214    SCROLLBAR_WIDTH = 18,
215    SCROLLBAR_SIDE = 19 // This property defines vertical scrollbar side (SCROLLBAR_LEFT_SIDE or SCROLLBAR_RIGHT_SIDE)
216 }
217 
218 /// ColorPicker
219 enum GuiColorPickerProperty {
220    COLOR_SELECTOR_SIZE = 16,
221    BAR_WIDTH = 17, // Lateral bar width
222    BAR_PADDING = 18, // Lateral bar separation from panel
223    BAR_SELECTOR_HEIGHT = 19, // Lateral bar selector height
224    BAR_SELECTOR_PADDING = 20 // Lateral bar selector outer padding
225 }
226 
227 /// Place enums into the global space.  Example: Allows usage of both KeyboardKey.KEY_RIGHT and KEY_RIGHT.
228 private string _enum(E...)() {
229    import std.format : formattedWrite;
230    import std.traits : EnumMembers;
231    import std.array : appender;
232 
233    auto writer = appender!string;
234    static foreach (T; E) {
235       static foreach (member; EnumMembers!T) {
236          writer.formattedWrite("alias %s = " ~ T.stringof ~ ".%s;\n", member, member);
237       }
238    }
239    return writer.data;
240 }
241 
242 mixin(_enum!(GuiControlState, GuiTextAlignment, GuiControl, GuiControlProperty, GuiDefaultProperty, GuiToggleProperty, GuiSliderProperty, GuiCheckBoxProperty,
243       GuiComboBoxProperty, GuiDropdownBoxProperty, GuiTextBoxProperty, GuiSpinnerProperty, GuiScrollBarProperty,
244       GuiScrollBarSide, GuiListViewProperty, GuiColorPickerProperty, GuiPropertyElement));
245 
246 // Required for: raygui icons
247 // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf()
248 // Required for: strlen() on GuiTextBox()
249 // Required for: va_list, va_start(), vfprintf(), va_end()
250 
251 // Gui control property style element
252 enum GuiPropertyElement {
253    BORDER = 0,
254    BASE = 1,
255    TEXT = 2,
256    OTHER = 3
257 }
258 
259 // Global Variables Definition
260 __gshared GuiControlState guiState;
261 __gshared Font guiFont; // NOTE: Highly coupled to raylib
262 __gshared bool guiLocked;
263 __gshared float guiAlpha = 1.0f;
264 
265 // Global gui style array (allocated on heap by default)
266 // NOTE: In raygui we manage a single int array with all the possible style properties.
267 // When a new style is loaded, it loads over the global style... but default gui style
268 // could always be recovered with GuiLoadStyleDefault()
269 __gshared uint[NUM_CONTROLS * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED)] guiStyle;
270 __gshared bool guiStyleLoaded;
271 
272 // Area of the currently active textbox
273 // Keeps state of the active textbox
274 
275 //----------------------------------------------------------------------------------
276 // Standalone Mode Functions Declaration
277 //
278 // NOTE: raygui depend on some raylib input and drawing functions
279 // To use raygui as standalone library, below functions must be defined by the user
280 //----------------------------------------------------------------------------------
281 
282 // White -- GuiColorBarAlpha()
283 // Black -- GuiColorBarAlpha()
284 // My own White (raylib logo)
285 // Gray -- GuiColorBarAlpha()
286 
287 // raylib functions already implemented in raygui
288 //-------------------------------------------------------------------------------
289 // Returns a Color struct from hexadecimal value
290 // Returns hexadecimal value for a Color
291 // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
292 // Check if point is inside rectangle
293 // Formatting of text with variables to 'embed'
294 //-------------------------------------------------------------------------------
295 
296 // Input required functions
297 //-------------------------------------------------------------------------------
298 
299 // -- GuiTextBox()
300 //-------------------------------------------------------------------------------
301 
302 // Drawing required functions
303 //-------------------------------------------------------------------------------
304 /* TODO */
305 
306 /* TODO */
307 
308 /* TODO */ // -- GuiColorPicker()
309 // -- GuiColorPicker()
310 // -- GuiColorPicker()
311 // -- GuiColorPicker()
312 
313 /* TODO */ // -- GuiDropdownBox()
314 /* TODO */ // -- GuiScrollBar()
315 
316 // -- GuiImageButtonEx()
317 //-------------------------------------------------------------------------------
318 
319 // Text required functions
320 //-------------------------------------------------------------------------------
321 // --  GetTextWidth()
322 
323 // -- GetTextWidth(), GuiTextBoxMulti()
324 // -- GuiDrawText()
325 //-------------------------------------------------------------------------------
326 
327 // RAYGUI_STANDALONE
328 
329 //----------------------------------------------------------------------------------
330 // Module specific Functions Declaration
331 //----------------------------------------------------------------------------------
332 
333 Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB
334 Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV
335 
336 // TODO: GetTextSize()
337 
338 /// Gui get text width using default font
339 private int GetTextWidth(string text) {
340    import std..string : empty, toStringz;
341 
342    Vector2 size = Vector2(0.0f, 0.0f);
343    if (!text.empty) {
344       size = MeasureTextEx(guiFont, text.toStringz, GuiGetStyle(GuiControl.DEFAULT, GuiDefaultProperty.TEXT_SIZE),
345             GuiGetStyle(GuiControl.DEFAULT, GuiDefaultProperty.TEXT_SPACING));
346    }
347    // TODO: Consider text icon width here???
348    return cast(int)size.x;
349 }
350 
351 /// Get text bounds considering control bounds
352 private Rectangle GetTextBounds(int control, in Rectangle bounds) {
353    immutable border = GuiGetStyle(control, GuiControlProperty.BORDER_WIDTH);
354    immutable padding = GuiGetStyle(control, GuiControlProperty.INNER_PADDING);
355    Rectangle textBounds = Rectangle(bounds.x + border + padding, bounds.y + border + padding,
356          bounds.width - 2 * (border + padding), bounds.height - 2 * (border + padding));
357    switch (control) {
358       case GuiControl.COMBOBOX:
359          textBounds.width -= (GuiGetStyle(control,
360                GuiComboBoxProperty.SELECTOR_WIDTH) + GuiGetStyle(control, GuiComboBoxProperty.SELECTOR_PADDING));
361          break;
362       case GuiControl.CHECKBOX:
363          textBounds.x += (bounds.width + GuiGetStyle(control, GuiCheckBoxProperty.CHECK_TEXT_PADDING));
364          break;
365       default:
366          break;
367    }
368    // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, SPINNER, LISTVIEW (scrollbar?)
369    // More special cases (label side): CHECKBOX, SLIDER
370    return textBounds;
371 
372 }
373 
374 /// Get text icon if provided and move text cursor
375 string GetTextIcon(string text, out int iconId) {
376    version (RAYGUI_RICONS_SUPPORT) {
377       //TODO: Work out what's going on here.
378       if (text[0] == "#") {
379          //.scan(/#(\d{3})#/).first.first.to_i
380       }
381    }
382    return text;
383 }
384 
385 /// Gui draw text using default font
386 void GuiDrawText(string text, Rectangle bounds, int alignment, Color tint) {
387    import std..string : empty, toStringz;
388 
389    enum ICON_TEXT_PADDING = 4;
390    if (!text.empty) {
391       int iconId;
392       text = GetTextIcon(text, iconId); // Check text for icon and move cursor
393 
394       // Get text position depending on alignment and iconId
395       //---------------------------------------------------------------------------------
396 
397       Vector2 position = Vector2(bounds.x, bounds.y);
398 
399       // NOTE: We get text size after icon been processed
400       int textWidth = GetTextWidth(text);
401       int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE);
402 
403       version (RAYGUI_RICONS_SUPPORT) {
404          if (iconId > 0) {
405             textWidth += RICONS_SIZE;
406             // WARNING: If only icon provided, text could be pointing to eof character!
407             if (!text.empty) {
408                textWidth += ICON_TEXT_PADDING;
409             }
410          }
411       }
412       // Check guiTextAlign global variables
413       switch (alignment) {
414          case GUI_TEXT_ALIGN_LEFT:
415             position.x = bounds.x;
416             position.y = bounds.y + bounds.height / 2 - textHeight / 2 + VALIGN_OFFSET(bounds.height);
417             break;
418          case GUI_TEXT_ALIGN_CENTER:
419             position.x = bounds.x + bounds.width / 2 - textWidth / 2;
420             position.y = bounds.y + bounds.height / 2 - textHeight / 2 + VALIGN_OFFSET(bounds.height);
421             break;
422          case GUI_TEXT_ALIGN_RIGHT:
423             position.x = bounds.x + bounds.width - textWidth;
424             position.y = bounds.y + bounds.height / 2 - textHeight / 2 + VALIGN_OFFSET(bounds.height);
425             break;
426          default:
427             break;
428       }
429       //---------------------------------------------------------------------------------
430 
431       // Draw text (with icon if available)
432       //---------------------------------------------------------------------------------
433       version (RAYGUI_RICONS_SUPPORT) {
434          if (iconId > 0) {
435             // NOTE: We consider icon height, probably different than text size
436             DrawIcon(iconId, Vector2(position.x, bounds.y + bounds.height / 2 - RICONS_SIZE / 2 + VALIGN_OFFSET(bounds.height)), 1, tint);
437             position.x += (RICONS_SIZE + ICON_TEXT_PADDING);
438          }
439       } else {
440          DrawTextEx(guiFont, text.toStringz, position, GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SPACING), tint);
441       }
442       //---------------------------------------------------------------------------------
443    }
444 }
445 
446 /// Enable gui global state
447 void GuiEnable() {
448    guiState = GuiControlState.GUI_STATE_NORMAL;
449 }
450 
451 /// Disable gui global state
452 void GuiDisable() {
453    guiState = GuiControlState.GUI_STATE_DISABLED;
454 }
455 
456 /// Lock gui global state
457 void GuiLock() {
458    guiLocked = true;
459 }
460 
461 /// Unlock gui global state
462 void GuiUnlock() {
463    guiLocked = false;
464 }
465 
466 /// Set gui state (global state)
467 void GuiState(int state) {
468    guiState = cast(GuiControlState)state;
469 }
470 
471 /// Define custom gui font
472 void GuiFont(Font font) {
473    if (font.texture.id > 0) {
474       guiFont = font;
475       GuiSetStyle(GuiControl.DEFAULT, GuiDefaultProperty.TEXT_SIZE, font.baseSize);
476    }
477 }
478 
479 /// Set gui controls alpha global state
480 void GuiFade(float alpha) {
481    import std.algorithm : clamp;
482 
483    guiAlpha = alpha.clamp(0.0f, 1.0f);
484 }
485 
486 /// Set control style property value
487 void GuiSetStyle(int control, int property, int value) {
488    if (!guiStyleLoaded) {
489       GuiLoadStyleDefault();
490    }
491    guiStyle[control * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property] = value;
492 }
493 
494 /**
495  * Get control style property value
496  * Params:
497  *  control = Control index
498  *  property = Property index
499  */
500 int GuiGetStyle(int control, int property) {
501    if (!guiStyleLoaded) {
502       GuiLoadStyleDefault();
503    }
504    return guiStyle[control * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + property];
505 }
506 
507 /// Window Box control
508 bool GuiWindowBox(Rectangle bounds, string text) {
509    enum WINDOW_CLOSE_BUTTON_PADDING = 2;
510    enum WINDOW_STATUSBAR_HEIGHT = 24;
511    GuiControlState state = guiState;
512    bool clicked;
513    immutable borderWidth = GuiGetStyle(GuiControl.DEFAULT, GuiControlProperty.BORDER_WIDTH);
514    immutable statusBar = Rectangle(bounds.x, bounds.y, bounds.width, WINDOW_STATUSBAR_HEIGHT);
515    immutable buttonRec = Rectangle(statusBar.x + statusBar.width - borderWidth - WINDOW_CLOSE_BUTTON_PADDING - 20,
516          statusBar.y + borderWidth + WINDOW_CLOSE_BUTTON_PADDING, 18, 18);
517    if (bounds.height < WINDOW_STATUSBAR_HEIGHT * 2)
518       bounds.height = WINDOW_STATUSBAR_HEIGHT * 2;
519    // Update control
520    //--------------------------------------------------------------------
521    // NOTE: Logic is directly managed by button
522    //--------------------------------------------------------------------
523 
524    // Draw control
525    //--------------------------------------------------------------------
526 
527    // Draw window base
528    DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(GuiControl.DEFAULT,
529          GuiPropertyElement.BORDER + (state * 3))), guiAlpha));
530    DrawRectangleRec(Rectangle(bounds.x + borderWidth, bounds.y + borderWidth, bounds.width - borderWidth * 2, bounds.height - borderWidth
531          * 2), Fade(GetColor(GuiGetStyle(GuiControl.DEFAULT, GuiDefaultProperty.BACKGROUND_COLOR)), guiAlpha));
532 
533    // Draw window header as status bar
534    immutable defaultPadding = GuiGetStyle(GuiControl.DEFAULT, GuiControlProperty.INNER_PADDING);
535    immutable defaultTextAlign = GuiGetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_ALIGNMENT);
536    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.INNER_PADDING, 8);
537    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_ALIGNMENT, GuiTextAlignment.GUI_TEXT_ALIGN_LEFT);
538    GuiStatusBar(statusBar, text);
539    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.INNER_PADDING, defaultPadding);
540    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_ALIGNMENT, defaultTextAlign);
541 
542    // Draw window close button
543    immutable tempBorderWidth = GuiGetStyle(GuiControl.BUTTON, GuiControlProperty.BORDER_WIDTH);
544    immutable tempTextAlignment = GuiGetStyle(GuiControl.BUTTON, GuiControlProperty.TEXT_ALIGNMENT);
545    GuiSetStyle(GuiControl.BUTTON, GuiControlProperty.BORDER_WIDTH, 1);
546    GuiSetStyle(GuiControl.BUTTON, GuiControlProperty.TEXT_ALIGNMENT, GuiTextAlignment.GUI_TEXT_ALIGN_CENTER);
547    version (RAYGUI_RICONS_SUPPORT) {
548       clicked = GuiButton(buttonRec, GuiIconText(RICON_CROSS_SMALL, NULL));
549    } else {
550       clicked = GuiButton(buttonRec, "x");
551    }
552    GuiSetStyle(GuiControl.BUTTON, GuiControlProperty.BORDER_WIDTH, tempBorderWidth);
553    GuiSetStyle(GuiControl.BUTTON, GuiControlProperty.TEXT_ALIGNMENT, tempTextAlignment);
554    //--------------------------------------------------------------------
555 
556    return clicked;
557 
558 }
559 
560 //// Group Box control with title name
561 void GuiGroupBox(Rectangle bounds, string text) {
562    enum GROUPBOX_LINE_THICK = 1;
563    immutable state = guiState;
564    immutable color = (state == GUI_STATE_DISABLED) ? BORDER_COLOR_DISABLED : LINE_COLOR;
565    auto fade = GuiGetStyle(GuiControl.DEFAULT, color).GetColor.Fade(guiAlpha);
566    // Draw control
567    //--------------------------------------------------------------------
568    DrawRectangle(cast(int)bounds.x, cast(int)bounds.y, GROUPBOX_LINE_THICK, cast(int)bounds.height, fade);
569    DrawRectangle(cast(int)bounds.x, cast(int)(bounds.y + bounds.height - 1), cast(int)bounds.width, GROUPBOX_LINE_THICK, fade);
570    DrawRectangle(cast(int)(bounds.x + bounds.width - 1), cast(int)bounds.y, GROUPBOX_LINE_THICK, cast(int)bounds.height, fade);
571    GuiLine(Rectangle(cast(int)bounds.x, cast(int)bounds.y, cast(int)bounds.width, 1), text);
572 }
573 
574 /// Line control
575 void GuiLine(Rectangle bounds, string text) {
576    import std..string : empty;
577 
578    enum LINE_TEXT_PADDING = 10;
579    enum LINE_TEXT_SPACING = 2;
580 
581    GuiControlState state = guiState;
582 
583    immutable color = (state == GUI_STATE_DISABLED) ? BORDER_COLOR_DISABLED : LINE_COLOR;
584    auto fade = GuiGetStyle(GuiControl.DEFAULT, color).GetColor.Fade(guiAlpha);
585 
586    // Draw control
587    //--------------------------------------------------------------------
588    if (text.empty) {
589       DrawRectangle(cast(int)bounds.x, cast(int)(bounds.y + bounds.height / 2), cast(int)bounds.width, 1, fade);
590    } else {
591       Rectangle textBounds;
592       textBounds.width = GetTextWidth(text) + 2 * LINE_TEXT_SPACING; // TODO: Consider text icon
593       textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE);
594       textBounds.x = bounds.x + LINE_TEXT_PADDING + LINE_TEXT_SPACING;
595       textBounds.y = bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2;
596 
597       // Draw line with embedded text label: "--- text --------------"
598       DrawRectangle(cast(int)bounds.x, cast(int)bounds.y, LINE_TEXT_PADDING, 1, fade);
599       GuiLabel(textBounds, text);
600       DrawRectangle(cast(int)(bounds.x + textBounds.width + LINE_TEXT_PADDING + 2 * LINE_TEXT_SPACING),
601             cast(int)bounds.y, cast(int)(bounds.width - (textBounds.width + LINE_TEXT_PADDING + 2 * LINE_TEXT_SPACING)), 1, fade);
602    }
603    //--------------------------------------------------------------------
604 }
605 
606 /// Panel control
607 void GuiPanel(Rectangle bounds) {
608    enum PANEL_BORDER_WIDTH = 1;
609    auto state = guiState;
610    // Draw control
611    //--------------------------------------------------------------------
612    DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED) ? BASE_COLOR_DISABLED
613          : BACKGROUND_COLOR)), guiAlpha));
614    DrawRectangleLinesEx(bounds, PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == GUI_STATE_DISABLED)
615          ? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha));
616    //--------------------------------------------------------------------
617 }
618 
619 /// Scroll Panel control
620 Rectangle GuiScrollPanel(Rectangle bounds, Rectangle content, ref Vector2 scroll) {
621    import std.algorithm : clamp;
622 
623    auto state = guiState;
624    Vector2 scrollPos = scroll;
625 
626    bool hasHorizontalScrollBar = (content.width > bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH)) ? true : false;
627    bool hasVerticalScrollBar = (content.height > bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH)) ? true : false;
628 
629    // Recheck to account for the other scrollbar being visible
630    if (!hasHorizontalScrollBar)
631       hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2 * GuiGetStyle(DEFAULT,
632             BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH)))) ? true : false;
633    if (!hasVerticalScrollBar)
634       hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2 * GuiGetStyle(DEFAULT,
635             BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH)))) ? true : false;
636 
637    const horizontalScrollBarWidth = hasHorizontalScrollBar ? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
638    const verticalScrollBarWidth = hasVerticalScrollBar ? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
639    const Rectangle horizontalScrollBar = {
640       ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? bounds.x + verticalScrollBarWidth : bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH),
641          bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH),
642          bounds.width - verticalScrollBarWidth - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH), horizontalScrollBarWidth
643    };
644    const Rectangle verticalScrollBar = {
645       ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH)
646             : bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), bounds.y + GuiGetStyle(DEFAULT,
647             BORDER_WIDTH), verticalScrollBarWidth, bounds.height - horizontalScrollBarWidth - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH)
648    };
649 
650    // Calculate view area (area without the scrollbars)
651    Rectangle view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? Rectangle(bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT,
652          BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2 * GuiGetStyle(DEFAULT,
653          BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)
654       : Rectangle(bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH),
655             bounds.width - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth,
656             bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth);
657 
658    // Clip view area to the actual content size
659    view.width = view.width.clamp(0, content.width);
660    view.height = view.height.clamp(0, content.height);
661 
662    // TODO: Review!
663    const horizontalMin = hasHorizontalScrollBar ? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)
664          ? -verticalScrollBarWidth : 0) - GuiGetStyle(DEFAULT, BORDER_WIDTH) : ((GuiGetStyle(LISTVIEW,
665          SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? -verticalScrollBarWidth : 0) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
666    const horizontalMax = hasHorizontalScrollBar ? content.width - bounds.width + verticalScrollBarWidth + GuiGetStyle(DEFAULT,
667          BORDER_WIDTH) - ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE) ? verticalScrollBarWidth : 0)
668       : -GuiGetStyle(DEFAULT, BORDER_WIDTH);
669    const verticalMin = hasVerticalScrollBar ? -GuiGetStyle(DEFAULT, BORDER_WIDTH) : -GuiGetStyle(DEFAULT, BORDER_WIDTH);
670    const verticalMax = hasVerticalScrollBar ? content.height - bounds.height + horizontalScrollBarWidth + GuiGetStyle(DEFAULT,
671          BORDER_WIDTH) : -GuiGetStyle(DEFAULT, BORDER_WIDTH);
672 
673    // Update control
674    //--------------------------------------------------------------------
675    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
676       auto mousePoint = GetMousePosition();
677 
678       // Check button state
679       if (CheckCollisionPointRec(mousePoint, bounds)) {
680          if (IsMouseButtonDown(MouseButton.MOUSE_LEFT_BUTTON))
681             state = GUI_STATE_PRESSED;
682          else
683             state = GUI_STATE_FOCUSED;
684 
685          if (hasHorizontalScrollBar) {
686             if (IsKeyDown(KeyboardKey.KEY_RIGHT))
687                scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
688             if (IsKeyDown(KeyboardKey.KEY_LEFT))
689                scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
690          }
691 
692          if (hasVerticalScrollBar) {
693             if (IsKeyDown(KeyboardKey.KEY_DOWN))
694                scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
695             if (IsKeyDown(KeyboardKey.KEY_UP))
696                scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
697          }
698 
699          scrollPos.y += GetMouseWheelMove() * 20;
700       }
701    }
702 
703    // Normalize scroll values
704    if (scrollPos.x > -horizontalMin)
705       scrollPos.x = -horizontalMin;
706    if (scrollPos.x < -horizontalMax)
707       scrollPos.x = -horizontalMax;
708    if (scrollPos.y > -verticalMin)
709       scrollPos.y = -verticalMin;
710    if (scrollPos.y < -verticalMax)
711       scrollPos.y = -verticalMax;
712    //--------------------------------------------------------------------
713 
714    // Draw control
715    //--------------------------------------------------------------------
716    DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background
717 
718    // Save size of the scrollbar slider
719    const int slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE);
720 
721    // Draw horizontal scrollbar if visible
722    if (hasHorizontalScrollBar) {
723       // Change scrollbar slider size to show the diff in size between the content width and the widget width
724       GuiSetStyle(SCROLLBAR, SLIDER_SIZE, cast(int)(((bounds.width - 2 * GuiGetStyle(DEFAULT,
725             BORDER_WIDTH) - verticalScrollBarWidth) / content.width) * (bounds.width - 2 * GuiGetStyle(DEFAULT,
726             BORDER_WIDTH) - verticalScrollBarWidth)));
727       scrollPos.x = -GuiScrollBar(horizontalScrollBar, cast(int)-scrollPos.x, cast(int)horizontalMin, cast(int)horizontalMax);
728    }
729 
730    // Draw vertical scrollbar if visible
731    if (hasVerticalScrollBar) {
732       // Change scrollbar slider size to show the diff in size between the content height and the widget height
733       GuiSetStyle(SCROLLBAR, SLIDER_SIZE, cast(int)(((bounds.height - 2 * GuiGetStyle(DEFAULT,
734             BORDER_WIDTH) - horizontalScrollBarWidth) / content.height) * (bounds.height - 2 * GuiGetStyle(DEFAULT,
735             BORDER_WIDTH) - horizontalScrollBarWidth)));
736       scrollPos.y = -GuiScrollBar(verticalScrollBar, cast(int)-scrollPos.y, cast(int)verticalMin, cast(int)verticalMax);
737    }
738 
739    // Draw detail corner rectangle if both scroll bars are visible
740    if (hasHorizontalScrollBar && hasVerticalScrollBar) {
741       // TODO: Consider scroll bars side
742       DrawRectangleRec(Rectangle(horizontalScrollBar.x + horizontalScrollBar.width + 2,
743             verticalScrollBar.y + verticalScrollBar.height + 2, horizontalScrollBarWidth - 4, verticalScrollBarWidth - 4),
744             Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state * 3))), guiAlpha));
745    }
746 
747    // Set scrollbar slider size back to the way it was before
748    GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider);
749 
750    // Draw scrollbar lines depending on current state
751    DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + (state * 3))), guiAlpha));
752    //--------------------------------------------------------------------
753 
754    scroll = scrollPos;
755 
756    return view;
757 }
758 
759 /// Label control
760 void GuiLabel(Rectangle bounds, string text) {
761    auto state = guiState;
762    immutable color = (state == GUI_STATE_DISABLED) ? TEXT_COLOR_DISABLED : TEXT_COLOR_NORMAL;
763    auto fade = GuiGetStyle(GuiControl.LABEL, color).GetColor.Fade(guiAlpha);
764    // Update control
765    //--------------------------------------------------------------------
766    // ...
767    //--------------------------------------------------------------------
768 
769    // Draw control
770    //--------------------------------------------------------------------
771    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), fade);
772    //--------------------------------------------------------------------
773 }
774 
775 ///
776 /**
777  * Button control
778  *
779  * Params:
780  *  bounds = Button bounds
781  *  text = Text inside button
782  *
783  *
784  * Returns:  true when clicked
785  *
786  */
787 bool GuiButton(Rectangle bounds, string text) {
788    GuiControlState state = guiState;
789    immutable(int) borderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
790    immutable(int) pressed = bounds.LeftClicked(state);
791 
792    // Draw control
793    /+
794    DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state * 3))), guiAlpha));
795    DrawRectangleRec(Rectangle(bounds.x + borderWidth, bounds.y + borderWidth, bounds.width - 2 * borderWidth, bounds.height - 2
796          * borderWidth), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state * 3))), guiAlpha));
797    GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT),
798          Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state * 3))), guiAlpha));
799 +/
800    DrawRectangleLinesEx(bounds, borderWidth, Fade(getBtnColor!BORDER(state), guiAlpha));
801    DrawRectangleRec(Rectangle(bounds.x + borderWidth, bounds.y + borderWidth, bounds.width - 2 * borderWidth, bounds.height - 2
802          * borderWidth), Fade(getBtnColor!BASE(state), guiAlpha));
803    GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT),
804          Fade(getBtnColor!TEXT(state), guiAlpha));
805 
806    return pressed;
807 }
808 private Color getBtnColor(GuiPropertyElement E)(GuiControlState state) {
809    return GetColor(GuiGetStyle(BUTTON, E + (state * 3)));
810 }
811 
812 private Fade getBtnFade(GuiPropertyElement E)(GuiControlState state, float alpha) {
813    return Fade(getBtnColor!E(state), alpha);
814 }
815 
816 
817 /// Determine whether something has been clicked.
818 private bool LeftClicked(in Rectangle bounds, ref GuiControlState state) {
819    bool clicked;
820    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
821       auto mousePoint = GetMousePosition();
822       // Check checkbox state
823       if (CheckCollisionPointRec(mousePoint, bounds)) {
824          if (IsMouseButtonDown(MouseButton.MOUSE_LEFT_BUTTON)) {
825             state = GUI_STATE_PRESSED;
826          } else if (IsMouseButtonReleased(MouseButton.MOUSE_LEFT_BUTTON)) {
827             clicked = true;
828          } else {
829             state = GUI_STATE_FOCUSED;
830          }
831       }
832    }
833    return clicked;
834 }
835 
836 /// Label button control
837 bool GuiLabelButton(Rectangle bounds, string text) {
838    auto state = guiState;
839    auto clicked = bounds.LeftClicked(state);
840    auto fade = GuiGetStyle(LABEL, TEXT + (state * 3)).GetColor.Fade(guiAlpha);
841 
842    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), fade);
843 
844    return clicked;
845 }
846 
847 /// Image button control, returns true when clicked
848 bool GuiImageButton(Rectangle bounds, Texture2D texture) {
849    return GuiImageButtonEx(bounds, texture, Rectangle(0, 0, texture.width, texture.height), "");
850 }
851 
852 /// Image button control, returns true when clicked
853 bool GuiImageButtonEx(Rectangle bounds, Texture2D texture, Rectangle texSource, string text) {
854    auto state = guiState;
855    auto clicked = bounds.LeftClicked(state);
856 
857    DrawRectangleLinesEx(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state * 3))), guiAlpha));
858    DrawRectangle(cast(int)(bounds.x + GuiGetStyle(BUTTON, BORDER_WIDTH)), cast(int)(bounds.y + GuiGetStyle(BUTTON,
859          BORDER_WIDTH)), cast(int)(bounds.width - 2 * GuiGetStyle(BUTTON, BORDER_WIDTH)),
860          cast(int)(bounds.height - 2 * GuiGetStyle(BUTTON, BORDER_WIDTH)), Fade(GetColor(GuiGetStyle(BUTTON,
861             BASE + (state * 3))), guiAlpha));
862 
863    if (text !is "") {
864       GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT),
865             Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state * 3))), guiAlpha));
866    }
867    if (texture.id > 0) {
868       DrawTextureRec(texture, texSource, Vector2(bounds.x + bounds.width / 2 - (texSource.width + GuiGetStyle(BUTTON,
869             INNER_PADDING) / 2) / 2, bounds.y + bounds.height / 2 - texSource.height / 2),
870             Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state * 3))), guiAlpha));
871    }
872    return clicked;
873 }
874 
875 /// Toggle Button control, returns true when active
876 bool GuiToggle(Rectangle bounds, string text, bool active) {
877    auto state = guiState;
878    immutable borderWidth = GuiGetStyle(TOGGLE, BORDER_WIDTH);
879    immutable textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT);
880    // Update control.
881    if (bounds.LeftClicked(state)) {
882       active = !active;
883    }
884 
885    immutable Rectangle toggleView = {
886       bounds.x + borderWidth, bounds.y + borderWidth, bounds.width - 2 * borderWidth, bounds.height - 2 * borderWidth
887    };
888    if (state == GUI_STATE_NORMAL) {
889       DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(TOGGLE, (active ? BORDER_COLOR_PRESSED
890             : (BORDER + state * 3)))), guiAlpha));
891       DrawRectangleRec(toggleView, Fade(GetColor(GuiGetStyle(TOGGLE, (active ? BASE_COLOR_PRESSED : (BASE + state * 3)))), guiAlpha));
892 
893       GuiDrawText(text, GetTextBounds(TOGGLE, bounds), textAlignment, Fade(GetColor(GuiGetStyle(TOGGLE, (active
894             ? TEXT_COLOR_PRESSED : (TEXT + state * 3)))), guiAlpha));
895    } else {
896       DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state * 3)), guiAlpha));
897       DrawRectangleRec(toggleView, Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state * 3)), guiAlpha));
898       GuiDrawText(text, GetTextBounds(TOGGLE, bounds), textAlignment, Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state * 3)), guiAlpha));
899    }
900    return active;
901 }
902 
903 /// Toggle Group control, returns toggled button index
904 int GuiToggleGroup(Rectangle bounds, string text, int active) {
905    immutable initBoundsX = bounds.x;
906    immutable padding = GuiGetStyle(TOGGLE, GROUP_PADDING);
907    // Get substrings elements from text (elements pointers)
908    string[] elements = text.GuiTextSplit();
909    string prevRow = elements[0];
910    foreach (i, element; elements) {
911       if (prevRow != element) {
912          bounds.x = initBoundsX;
913          bounds.y += (bounds.height + padding);
914          prevRow = element;
915       }
916 
917       if (i == active) {
918          GuiToggle(bounds, element, true);
919       } else if (GuiToggle(bounds, element, false) == true) {
920          // FIX: size_t active ??
921          active = cast(int)i;
922       }
923 
924       bounds.x += (bounds.width + padding);
925    }
926    return active;
927 }
928 
929 /// Check Box control, returns true when active
930 bool GuiCheckBox(Rectangle bounds, string text, bool checked) {
931    auto state = guiState;
932    Rectangle textBounds;
933    immutable textPadding = GuiGetStyle(CHECKBOX, CHECK_TEXT_PADDING);
934    immutable controlPadding = GuiGetStyle(CHECKBOX, INNER_PADDING);
935    immutable borderWidth = GuiGetStyle(CHECKBOX, BORDER_WIDTH);
936    textBounds.x = bounds.x + bounds.width + textPadding;
937    textBounds.y = bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2;
938    textBounds.width = GetTextWidth(text); // TODO: Consider text icon
939    textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE);
940 
941    // Update control
942    if (bounds.LeftClicked(state)) {
943       checked = !checked;
944    }
945    // Draw control
946    DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state * 3))), guiAlpha));
947    if (checked) {
948       DrawRectangleRec(Rectangle(bounds.x + borderWidth + controlPadding, bounds.y + borderWidth + controlPadding,
949             bounds.width - 2 * (borderWidth + controlPadding), bounds.height - 2 * (borderWidth + controlPadding)),
950             Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state * 3)), guiAlpha));
951    }
952 
953    // NOTE: Forced left text alignment
954    GuiDrawText(text, textBounds, GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state * 3))), guiAlpha));
955    return checked;
956 }
957 
958 /// Combo Box control, returns selected item index
959 int GuiComboBox(Rectangle bounds, string text, int active) {
960    import std.algorithm : clamp;
961    import std..string : fromStringz;
962 
963    auto state = guiState;
964    immutable comboWidth = GuiGetStyle(COMBOBOX, SELECTOR_WIDTH);
965    immutable comboPadding = GuiGetStyle(COMBOBOX, SELECTOR_PADDING);
966    immutable comboBorderWidth = GuiGetStyle(COMBOBOX, BORDER_WIDTH);
967    bounds.width -= (comboWidth + comboPadding);
968 
969    Rectangle selector = Rectangle(bounds.x + bounds.width + comboPadding, bounds.y, comboWidth, bounds.height);
970 
971    // Get substrings elements from text (elements pointers, lengths and count)
972    auto elements = text.GuiTextSplit();
973    immutable elementCount = (elements.length - 1) > 0 ? elements.length - 1 : 0;
974    active = active.clamp(0, elementCount);
975 
976    // Update control
977    if (bounds.LeftClicked(state)) {
978       active = ((active + 1) >= elements.length) ? 0 : active + 1;
979    }
980 
981    // Draw combo box main
982    DrawRectangleLinesEx(bounds, comboBorderWidth, Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state * 3))), guiAlpha));
983    DrawRectangle(cast(int)(bounds.x + comboBorderWidth), cast(int)(bounds.y + comboBorderWidth),
984          cast(int)(bounds.width - 2 * comboBorderWidth), cast(int)(bounds.height - 2 * comboBorderWidth),
985          Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state * 3))), guiAlpha));
986 
987    GuiDrawText(elements[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT),
988          Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state * 3))), guiAlpha));
989 
990    // Draw selector using a custom button
991    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
992    immutable tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
993    immutable tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
994    GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
995    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
996    GuiButton(selector, cast(string)TextFormat("%i/%i", active + 1, elements.length).fromStringz);
997 
998    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
999    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
1000    return active;
1001 }
1002 
1003 /// Dropdown Box control, returns selected item
1004 bool GuiDropdownBox(Rectangle bounds, string text, ref int active, bool editMode) {
1005    auto state = guiState;
1006    bool pressed;
1007    int auxActive = active;
1008    const elements = text.GuiTextSplit();
1009    Rectangle closeBounds = bounds;
1010    Rectangle openBounds = bounds;
1011    openBounds.height *= elements.length + 1;
1012    if (guiLocked && editMode) {
1013       guiLocked = false;
1014    }
1015    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
1016       auto mousePoint = GetMousePosition();
1017       if (editMode) {
1018          state = GUI_STATE_PRESSED;
1019       }
1020       if (!editMode) {
1021          if (CheckCollisionPointRec(mousePoint, closeBounds)) {
1022             if (IsMouseButtonDown(MouseButton.MOUSE_LEFT_BUTTON)) {
1023                state = GUI_STATE_PRESSED;
1024             }
1025             if (IsMouseButtonPressed(MouseButton.MOUSE_LEFT_BUTTON)) {
1026                pressed = true;
1027             } else {
1028                state = GUI_STATE_FOCUSED;
1029             }
1030          }
1031       } else {
1032          if (CheckCollisionPointRec(mousePoint, closeBounds)) {
1033             if (IsMouseButtonPressed(MouseButton.MOUSE_LEFT_BUTTON)) {
1034                pressed = true;
1035             }
1036          } else if (!CheckCollisionPointRec(mousePoint, openBounds)) {
1037             if (IsMouseButtonPressed(MouseButton.MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MouseButton.MOUSE_LEFT_BUTTON)) {
1038                pressed = true;
1039             }
1040          }
1041       }
1042    }
1043    // Draw control
1044    //--------------------------------------------------------------------
1045 
1046    // TODO: Review this ugly hack... DROPDOWNBOX depends on GuiListElement() that uses DEFAULT_TEXT_ALIGNMENT
1047    immutable tempTextAlign = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
1048    immutable dropDownPadding = GuiGetStyle(DROPDOWNBOX, INNER_PADDING);
1049    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT));
1050    switch (state) {
1051       case GUI_STATE_NORMAL:
1052          DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_NORMAL)), guiAlpha));
1053          DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX,
1054                BORDER_COLOR_NORMAL)), guiAlpha));
1055          GuiListElement(bounds, elements[auxActive], false, false);
1056          break;
1057       case GUI_STATE_FOCUSED:
1058          GuiListElement(bounds, elements[auxActive], false, editMode);
1059          break;
1060       case GUI_STATE_PRESSED:
1061          if (!editMode) {
1062             GuiListElement(bounds, elements[auxActive], true, true);
1063          }
1064          if (editMode) {
1065             GuiPanel(openBounds);
1066             GuiListElement(bounds, elements[auxActive], true, true);
1067             foreach (i, element; elements) {
1068                Rectangle listRect = {
1069                   bounds.x, bounds.y + bounds.height * (i + 1) + dropDownPadding, bounds.width, bounds.height - dropDownPadding
1070                };
1071                if (i == auxActive && editMode) {
1072                   if (GuiListElement(listRect, element, true, true) == false) {
1073                      pressed = true;
1074                   }
1075                } else {
1076                   if (GuiListElement(listRect, element, false, true)) {
1077                      auxActive = cast(int)i;
1078                      pressed = true;
1079                   }
1080                }
1081             }
1082          }
1083          break;
1084       case GUI_STATE_DISABLED:
1085          DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_DISABLED)), guiAlpha));
1086          DrawRectangleLinesEx(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX,
1087                BORDER_COLOR_DISABLED)), guiAlpha));
1088          GuiListElement(bounds, elements[auxActive], false, false);
1089          break;
1090       default:
1091          break;
1092    }
1093 
1094    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, tempTextAlign);
1095    immutable arrowPadding = GuiGetStyle(DROPDOWNBOX, ARROW_RIGHT_PADDING);
1096    // TODO: Avoid this function, use icon instead or 'v'
1097    DrawTriangle(Vector2(bounds.x + bounds.width - arrowPadding, bounds.y + bounds.height / 2 - 2),
1098          Vector2(bounds.x + bounds.width - arrowPadding + 5, bounds.y + bounds.height / 2 - 2 + 5),
1099          Vector2(bounds.x + bounds.width - arrowPadding + 10, bounds.y + bounds.height / 2 - 2),
1100          Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state * 3))), guiAlpha));
1101    //--------------------------------------------------------------------
1102 
1103    active = auxActive;
1104    return pressed;
1105 }
1106 
1107 /// Spinner control, returns selected value
1108 bool GuiSpinner(Rectangle bounds, ref int value, int minValue, int maxValue, bool editMode) {
1109    import std.algorithm : clamp;
1110 
1111    bool pressed;
1112    int tempValue = value;
1113    immutable selectWidth = GuiGetStyle(SPINNER, SELECT_BUTTON_WIDTH);
1114    immutable selectPadding = GuiGetStyle(SPINNER, SELECT_BUTTON_PADDING);
1115    Rectangle spinner = {
1116       bounds.x + selectWidth + selectPadding, bounds.y, bounds.width - 2 * (selectWidth + selectPadding), bounds.height
1117    };
1118    Rectangle leftButtonBound = {bounds.x, bounds.y, selectWidth, bounds.height};
1119    Rectangle rightButtonBound = {bounds.x + bounds.width - selectWidth, bounds.y, selectWidth, bounds.height};
1120 
1121    // Update control
1122    //--------------------------------------------------------------------
1123    if (!editMode) {
1124       tempValue = tempValue.clamp(minValue, maxValue);
1125    }
1126    //--------------------------------------------------------------------
1127 
1128    // Draw control
1129    //--------------------------------------------------------------------
1130    // TODO: Set Spinner properties for ValueBox
1131    pressed = GuiValueBox(spinner, tempValue, minValue, maxValue, editMode);
1132 
1133    // Draw value selector custom buttons
1134    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
1135    immutable tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
1136    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
1137 
1138    immutable tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
1139    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
1140 
1141    version (RAYGUI_RICONS_SUPPORT) {
1142       if (GuiButton(leftButtonBound, GuiIconText(RICON_ARROW_LEFT_FILL, NULL))) {
1143          tempValue--;
1144       }
1145       if (GuiButton(rightButtonBound, GuiIconText(RICON_ARROW_RIGHT_FILL, NULL))) {
1146          tempValue++;
1147       }
1148    } else {
1149       if (GuiButton(leftButtonBound, "<")) {
1150          tempValue--;
1151       }
1152       if (GuiButton(rightButtonBound, ">")) {
1153          tempValue++;
1154       }
1155    }
1156    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
1157    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
1158    //--------------------------------------------------------------------
1159 
1160    value = tempValue;
1161    return pressed;
1162 }
1163 
1164 /// Value Box control, updates input text with numbers
1165 bool GuiValueBox(Rectangle bounds, ref int value, int minValue, int maxValue, bool editMode) {
1166    import std.conv : to, parse;
1167    import std.algorithm : clamp;
1168 
1169    enum VALUEBOX_MAX_CHARS = 32;
1170    auto text = value.to!string;
1171    bool pressed = GuiTextBox(bounds, text, VALUEBOX_MAX_CHARS, editMode);
1172    try {
1173       value = text.parse!int;
1174    } catch (Exception) {
1175       value = minValue;
1176    }
1177    value = value.clamp(minValue, maxValue);
1178    return pressed;
1179 }
1180 
1181 /// Text Box control, updates input text
1182 bool GuiTextBox(Rectangle bounds, ref string text, int textSize, bool editMode) {
1183    static int framesCounter = 0; // Required for blinking cursor
1184    auto state = guiState;
1185    bool pressed;
1186    immutable borderWidth = GuiGetStyle(TEXTBOX, BORDER_WIDTH);
1187 
1188    // Update control
1189    //--------------------------------------------------------------------
1190    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
1191       auto mousePoint = GetMousePosition();
1192       if (editMode) {
1193          state = GUI_STATE_PRESSED;
1194          framesCounter++;
1195 
1196          immutable key = GetKeyPressed();
1197          int keyCount = cast(int)text.length;
1198 
1199          // Only allow keys in range [32..125]
1200          if (keyCount < (textSize - 1)) {
1201             immutable maxWidth = (bounds.width - (GuiGetStyle(DEFAULT, INNER_PADDING) * 2));
1202             if (text.GetTextWidth() < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) {
1203                if (((key >= 32) && (key <= 125)) || ((key >= 128) && (key < 255))) {
1204                   text ~= key;
1205                   keyCount++;
1206                }
1207             }
1208          }
1209 
1210          // Delete text
1211          if (keyCount > 0) {
1212             if (IsKeyPressed(KeyboardKey.KEY_BACKSPACE)) {
1213                text.length--;
1214                framesCounter = 0;
1215             } else if (IsKeyDown(KeyboardKey.KEY_BACKSPACE)) {
1216                if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter % 2) == 0) {
1217                   text.length--;
1218                }
1219             }
1220             import std.algorithm : clamp;
1221 
1222             text.length = text.length.clamp(0, textSize);
1223          }
1224       }
1225 
1226       if (!editMode) {
1227          if (CheckCollisionPointRec(mousePoint, bounds)) {
1228             state = GUI_STATE_FOCUSED;
1229             if (IsMouseButtonPressed(0)) {
1230                pressed = true;
1231             }
1232          }
1233       } else {
1234          if (IsKeyPressed(KeyboardKey.KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0))) {
1235             pressed = true;
1236          }
1237       }
1238 
1239       if (pressed) {
1240          framesCounter = 0;
1241       }
1242    }
1243    //--------------------------------------------------------------------
1244 
1245    // Draw control
1246    //--------------------------------------------------------------------
1247    DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha));
1248    if (state == GUI_STATE_PRESSED) {
1249       DrawRectangleRec(Rectangle(bounds.x + borderWidth, bounds.y + borderWidth, bounds.width - 2 * borderWidth,
1250             bounds.height - 2 * borderWidth), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
1251 
1252       // Draw blinking cursor
1253       if (editMode && ((framesCounter / 20) % 2 == 0)) {
1254          DrawRectangleRec(Rectangle(bounds.x + GuiGetStyle(TEXTBOX,
1255                INNER_PADDING) + GetTextWidth(text) + 2 + bounds.width / 2 * GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT),
1256                bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE), 1, GuiGetStyle(DEFAULT, TEXT_SIZE) * 2),
1257                Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha));
1258       }
1259    } else if (state == GUI_STATE_DISABLED) {
1260       DrawRectangleRec(Rectangle(bounds.x + borderWidth, bounds.y + borderWidth, bounds.width - 2 * borderWidth,
1261             bounds.height - 2 * borderWidth), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha));
1262    }
1263 
1264    GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT),
1265          Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state * 3))), guiAlpha));
1266    //--------------------------------------------------------------------
1267    return pressed;
1268 }
1269 
1270 /// Text Box control with multiple lines
1271 bool GuiTextBoxMulti(Rectangle bounds, ref string text, int textSize, bool editMode) {
1272    import std..string : lastIndexOf;
1273    import std..string : toStringz;
1274    import std.format : formattedWrite;
1275    import std.array : array, appender;
1276    import std.conv : to;
1277 
1278    static int framesCounter = 0; // Required for blinking cursor
1279    auto state = guiState;
1280    bool pressed = false;
1281 
1282    bool textHasChange = false;
1283    int currentLine = 0;
1284    immutable textBoxPadding = GuiGetStyle(TEXTBOX, INNER_PADDING);
1285    immutable textBoxSize = GuiGetStyle(DEFAULT, TEXT_SIZE);
1286    immutable textBoxBorderW = GuiGetStyle(TEXTBOX, BORDER_WIDTH);
1287    auto writer = appender!string;
1288    int newLineIndex = 0;
1289    auto end = textSize > text.length ? text.length : textSize;
1290    string newChar;
1291    writer.reserve(textSize + 2);
1292    // Update control
1293    //--------------------------------------------------------------------
1294    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
1295       auto mousePoint = GetMousePosition();
1296 
1297       if (editMode) {
1298          state = GUI_STATE_PRESSED;
1299 
1300          framesCounter++;
1301 
1302          int keyCount = cast(int)text.length;
1303          immutable maxWidth = (bounds.width - (textBoxPadding * 2));
1304          immutable maxHeight = (bounds.height - (textBoxPadding * 2));
1305 
1306          //numChars = TextFormat("%i/%i", keyCount, textSize - 1);
1307 
1308          // Only allow keys in range [32..125]
1309          if (keyCount < (textSize - 1)) {
1310             immutable key = GetKeyPressed();
1311 
1312             if (MeasureTextEx(guiFont, text.toStringz, textBoxSize, 1).y < (maxHeight - textBoxSize)) {
1313                if (IsKeyPressed(KeyboardKey.KEY_ENTER)) {
1314                   newChar = "\n";
1315                   keyCount++;
1316                } else if (((key >= 32) && (key <= 125)) || ((key >= 128) && (key < 255))) {
1317                   newChar = cast(string)[key];
1318                   keyCount++;
1319                   textHasChange = true;
1320                }
1321             } else if (GetTextWidth(text[text.lastIndexOf('\n') .. $]) < (maxWidth - textBoxSize)) {
1322                if (((key >= 32) && (key <= 125)) || ((key >= 128) && (key < 255))) {
1323                   newChar = cast(string)[key];
1324                   keyCount++;
1325                   textHasChange = true;
1326                }
1327             }
1328          }
1329 
1330          // Delete text
1331          if (keyCount > 0) {
1332             if (IsKeyPressed(KeyboardKey.KEY_BACKSPACE)) {
1333                keyCount--;
1334                end = keyCount;
1335                framesCounter = 0;
1336                textHasChange = true;
1337             } else if (IsKeyDown(KeyboardKey.KEY_BACKSPACE)) {
1338                if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter % 2) == 0) {
1339                   keyCount--;
1340                }
1341                end = keyCount;
1342                textHasChange = true;
1343             }
1344          }
1345 
1346          // Introduce automatic new line if necessary
1347          if (textHasChange) {
1348             textHasChange = false;
1349             if (text.GetTextWidth() >= maxWidth) {
1350                immutable lastSpace = text.lastIndexOf(" ");
1351                if (lastSpace != -1) {
1352                   newLineIndex = cast(int)(lastSpace + 1);
1353                } else {
1354                   newLineIndex = cast(int)(text.length - 1);
1355                }
1356             }
1357          }
1358          writer.formattedWrite("%s%s%s%s", text[0 .. newLineIndex], (newLineIndex > 0 ? "\n" : ""), text[newLineIndex .. end], newChar);
1359          text = writer.data;
1360          // Counting how many new lines
1361          foreach (c; text) {
1362             if (c == '\n') {
1363                currentLine++;
1364             }
1365          }
1366       }
1367 
1368       // Changing edit mode
1369       if (!editMode) {
1370          if (CheckCollisionPointRec(mousePoint, bounds)) {
1371             state = GUI_STATE_FOCUSED;
1372             if (IsMouseButtonPressed(0)) {
1373                pressed = true;
1374             }
1375          }
1376       } else {
1377          if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(0)) {
1378             pressed = true;
1379          }
1380       }
1381 
1382       if (pressed) {
1383          framesCounter = 0;
1384       }
1385    }
1386    //--------------------------------------------------------------------
1387 
1388    // Draw control
1389    //--------------------------------------------------------------------
1390    DrawRectangleLinesEx(bounds, textBoxBorderW, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state * 3))), guiAlpha));
1391 
1392    if (state == GUI_STATE_PRESSED) {
1393       DrawRectangleRec(Rectangle(bounds.x + textBoxBorderW, bounds.y + textBoxBorderW, bounds.width - 2 * textBoxBorderW,
1394             bounds.height - 2 * textBoxBorderW), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha));
1395 
1396       if (editMode) {
1397          if ((framesCounter / 20) % 2 == 0) {
1398             string line;
1399             if (currentLine > 0) {
1400                line = text[text.lastIndexOf('\n') .. $];
1401             } else {
1402                line = text;
1403             }
1404 
1405             // Draw text cursor
1406             DrawRectangleRec(Rectangle(bounds.x + textBoxBorderW + textBoxPadding + GetTextWidth(line),
1407                   bounds.y + textBoxBorderW + textBoxPadding / 2 + ((GuiGetStyle(DEFAULT,
1408                   TEXT_SIZE) + textBoxPadding) * currentLine), 1, GuiGetStyle(DEFAULT, TEXT_SIZE) + textBoxPadding),
1409                   Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_FOCUSED)), guiAlpha));
1410          }
1411       }
1412    } else if (state == GUI_STATE_DISABLED) {
1413       DrawRectangleRec(Rectangle(bounds.x + textBoxBorderW, bounds.y + textBoxBorderW, bounds.width - 2 * textBoxBorderW,
1414             bounds.height - 2 * textBoxBorderW), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha));
1415    }
1416    GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, GUI_TEXT_ALIGN_LEFT),
1417          Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state * 3))), guiAlpha));
1418    //--------------------------------------------------------------------
1419 
1420    return pressed;
1421 }
1422 
1423 /// Slider control with pro parameters
1424 /// NOTE: Other GuiSlider*() controls use this one
1425 float GuiSliderPro(Rectangle bounds, string text, float value, float minValue, float maxValue, int sliderWidth, bool showValue) {
1426    import std..string : fromStringz;
1427    import std.algorithm : clamp;
1428 
1429    auto state = guiState;
1430    immutable sliderBorderWidth = GuiGetStyle(SLIDER, BORDER_WIDTH);
1431    immutable sliderPadding = GuiGetStyle(SLIDER, INNER_PADDING);
1432    immutable sliderText = GuiGetStyle(DEFAULT, TEXT_SIZE);
1433    immutable sliderValue = cast(int)(((value - minValue) / (maxValue - minValue)) * (bounds.width - 2 * sliderBorderWidth));
1434 
1435    Rectangle slider = {
1436       bounds.x, bounds.y + sliderBorderWidth + sliderPadding, 0, bounds.height - 2 * sliderBorderWidth - 2 * sliderPadding
1437    };
1438 
1439    if (sliderWidth > 0) // Slider
1440    {
1441       slider.x += (sliderValue - sliderWidth / 2);
1442       slider.width = sliderWidth;
1443    } else if (sliderWidth == 0) // SliderBar
1444    {
1445       slider.x += sliderBorderWidth;
1446       slider.width = sliderValue;
1447    }
1448 
1449    Rectangle textBounds = {0};
1450    textBounds.width = GetTextWidth(text); // TODO: Consider text icon
1451    textBounds.height = GuiGetStyle(DEFAULT, TEXT_SIZE);
1452    textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
1453    textBounds.y = bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2;
1454 
1455    // Update control
1456    //--------------------------------------------------------------------
1457    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
1458       auto mousePoint = GetMousePosition();
1459 
1460       if (CheckCollisionPointRec(mousePoint, bounds)) {
1461          if (IsMouseButtonDown(MouseButton.MOUSE_LEFT_BUTTON)) {
1462             state = GUI_STATE_PRESSED;
1463 
1464             // Get equivalent value and slider position from mousePoint.x
1465             value = ((maxValue - minValue) * (mousePoint.x - cast(float)(bounds.x + sliderWidth / 2))) / cast(float)(
1466                   bounds.width - sliderWidth) + minValue;
1467 
1468             if (sliderWidth > 0)
1469                slider.x = mousePoint.x - slider.width / 2; // Slider
1470             else if (sliderWidth == 0)
1471                slider.width = sliderValue; // SliderBar
1472          } else {
1473             state = GUI_STATE_FOCUSED;
1474          }
1475       }
1476       value = value.clamp(minValue, maxValue);
1477    }
1478 
1479    // Bar limits check
1480    if (sliderWidth > 0) // Slider
1481    {
1482       if (slider.x <= (bounds.x + sliderBorderWidth))
1483          slider.x = bounds.x + sliderBorderWidth;
1484       else if ((slider.x + slider.width) >= (bounds.x + bounds.width))
1485          slider.x = bounds.x + bounds.width - slider.width - sliderBorderWidth;
1486    } else if (sliderWidth == 0) // SliderBar
1487    {
1488       if (slider.width > bounds.width)
1489          slider.width = bounds.width - 2 * sliderBorderWidth;
1490    }
1491    //--------------------------------------------------------------------
1492 
1493    // Draw control
1494    //--------------------------------------------------------------------
1495    DrawRectangleLinesEx(bounds, sliderBorderWidth, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state * 3))), guiAlpha));
1496    DrawRectangleRec(Rectangle(bounds.x + sliderBorderWidth, bounds.y + sliderBorderWidth,
1497          bounds.width - 2 * sliderBorderWidth, bounds.height - 2 * sliderBorderWidth), Fade(GetColor(GuiGetStyle(SLIDER,
1498          (state != GUI_STATE_DISABLED) ? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha));
1499 
1500    // Draw slider internal bar (depends on state)
1501    if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED))
1502       DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha));
1503    else if (state == GUI_STATE_FOCUSED)
1504       DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha));
1505 
1506    GuiDrawText(text, textBounds, GuiGetStyle(SLIDER, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state * 3))), guiAlpha));
1507 
1508    // TODO: Review showValue parameter, really ugly...
1509    if (showValue)
1510       GuiDrawText(cast(string)TextFormat("%.02f", value).fromStringz,
1511             Rectangle(bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING),
1512                bounds.y + bounds.height / 2 - sliderText / 2 + sliderPadding, sliderText, sliderText),
1513             GUI_TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state * 3))), guiAlpha));
1514    //--------------------------------------------------------------------
1515 
1516    return value;
1517 }
1518 
1519 /// Slider control extended, returns selected value and has text
1520 float GuiSlider(Rectangle bounds, string text, float value, float minValue, float maxValue, bool showValue) {
1521    return GuiSliderPro(bounds, text, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH), showValue);
1522 }
1523 
1524 /// Slider Bar control extended, returns selected value
1525 float GuiSliderBar(Rectangle bounds, string text, float value, float minValue, float maxValue, bool showValue) {
1526    return GuiSliderPro(bounds, text, value, minValue, maxValue, 0, showValue);
1527 }
1528 
1529 /// Progress Bar control extended, shows current progress value
1530 float GuiProgressBar(Rectangle bounds, string text, float value, float minValue, float maxValue, bool showValue) {
1531    import std..string : fromStringz;
1532 
1533    auto state = guiState;
1534    immutable borderWidth = GuiGetStyle(PROGRESSBAR, BORDER_WIDTH);
1535    immutable progressPadding = GuiGetStyle(PROGRESSBAR, INNER_PADDING);
1536    Rectangle progress = {
1537       bounds.x + borderWidth, bounds.y + borderWidth + progressPadding, 0, bounds.height - 2 * borderWidth - 2 * progressPadding
1538    };
1539 
1540    // Update control
1541    //--------------------------------------------------------------------
1542    if (state != GUI_STATE_DISABLED)
1543       progress.width = cast(int)(value / (maxValue - minValue) * cast(float)(bounds.width - 2 * borderWidth));
1544    //--------------------------------------------------------------------
1545 
1546    // Draw control
1547    //--------------------------------------------------------------------
1548    if (showValue)
1549       GuiLabel(Rectangle(bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING),
1550             bounds.y + bounds.height / 2 - GuiGetStyle(DEFAULT, TEXT_SIZE) / 2 + GuiGetStyle(SLIDER, INNER_PADDING),
1551             GuiGetStyle(DEFAULT, TEXT_SIZE), GuiGetStyle(DEFAULT, TEXT_SIZE)), cast(string)TextFormat("%.02f", value).fromStringz);
1552 
1553    DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state * 3))), guiAlpha));
1554 
1555    // Draw slider internal progress bar (depends on state)
1556    if ((state == GUI_STATE_NORMAL) || (state == GUI_STATE_PRESSED))
1557       DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha));
1558    else if (state == GUI_STATE_FOCUSED)
1559       DrawRectangleRec(progress, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha));
1560    //--------------------------------------------------------------------
1561 
1562    return value;
1563 }
1564 
1565 // Status Bar control
1566 void GuiStatusBar(Rectangle bounds, string text) {
1567    auto state = guiState;
1568 
1569    // Draw control
1570    //--------------------------------------------------------------------
1571    DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DEFAULT,
1572          (state != GUI_STATE_DISABLED) ? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha));
1573    DrawRectangleRec(Rectangle(bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT,
1574          BORDER_WIDTH), bounds.width - GuiGetStyle(DEFAULT, BORDER_WIDTH) * 2, bounds.height - GuiGetStyle(DEFAULT, BORDER_WIDTH) * 2),
1575          Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED) ? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha));
1576    GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GuiGetStyle(DEFAULT, TEXT_ALIGNMENT),
1577          Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED) ? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha));
1578    //--------------------------------------------------------------------
1579 }
1580 
1581 /// Dummy rectangle control, intended for placeholding
1582 void GuiDummyRec(Rectangle bounds, string text) {
1583    auto state = guiState;
1584    // Update control
1585    //--------------------------------------------------------------------
1586    bounds.LeftClicked(state);
1587    //--------------------------------------------------------------------
1588 
1589    // Draw control
1590    //--------------------------------------------------------------------
1591    DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, (state != GUI_STATE_DISABLED) ? BASE_COLOR_NORMAL
1592          : BASE_COLOR_DISABLED)), guiAlpha));
1593    GuiDrawText(text, GetTextBounds(DEFAULT, bounds), GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON,
1594          (state != GUI_STATE_DISABLED) ? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha));
1595 }
1596 
1597 /// Scroll Bar control
1598 int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) {
1599    import std.algorithm : clamp;
1600 
1601    auto state = guiState;
1602    immutable scrollBarWidth = GuiGetStyle(SCROLLBAR, BORDER_WIDTH);
1603    immutable scrollBarPadding = GuiGetStyle(SCROLLBAR, INNER_PADDING);
1604    immutable sliderPadding = GuiGetStyle(SCROLLBAR, SLIDER_PADDING);
1605    float sliderSize = GuiGetStyle(SCROLLBAR, SLIDER_SIZE);
1606 
1607    // Is the scrollbar horizontal or vertical?
1608    immutable isVertical = (bounds.width > bounds.height) ? false : true;
1609 
1610    // The size (width or height depending on scrollbar type) of the spinner buttons
1611    immutable spinnerSize = GuiGetStyle(SCROLLBAR, SHOW_SPINNER_BUTTONS) ? (isVertical ? bounds.width - 2 * scrollBarWidth
1612          : bounds.height - 2 * scrollBarWidth) : 0;
1613 
1614    // Spinner buttons [<] [>] [∧] [∨]
1615    Rectangle spinnerUpLeft, spinnerDownRight;
1616    // Actual area of the scrollbar excluding the spinner buttons
1617    Rectangle scrollbar; //  ------------
1618    // Slider bar that moves     --[///]-----
1619    Rectangle slider;
1620 
1621    // Normalize value
1622    value = value.clamp(minValue, maxValue);
1623 
1624    immutable range = maxValue - minValue;
1625    // Calculate rectangles for all of the components
1626    spinnerUpLeft = Rectangle(bounds.x + scrollBarWidth, bounds.y + scrollBarWidth, spinnerSize, spinnerSize);
1627 
1628    if (isVertical) {
1629       spinnerDownRight = Rectangle(bounds.x + scrollBarWidth, bounds.y + bounds.height - spinnerSize - scrollBarWidth,
1630             spinnerSize, spinnerSize);
1631       scrollbar = Rectangle((bounds.x + scrollBarWidth + scrollBarPadding), spinnerUpLeft.y + spinnerUpLeft.height,
1632             bounds.width - 2 * (scrollBarWidth + scrollBarPadding),
1633             bounds.height - spinnerUpLeft.height - spinnerDownRight.height - 2 * scrollBarWidth);
1634       sliderSize = (sliderSize >= scrollbar.height) ? (scrollbar.height - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar
1635       slider = Rectangle(bounds.x + scrollBarWidth + sliderPadding,
1636             scrollbar.y + cast(int)((cast(float)(value - minValue) / range) * (scrollbar.height - sliderSize)),
1637             bounds.width - 2 * (scrollBarWidth + sliderPadding), sliderSize);
1638    } else {
1639       spinnerDownRight = Rectangle(bounds.x + bounds.width - spinnerSize - scrollBarWidth, bounds.y + scrollBarWidth,
1640             spinnerSize, spinnerSize);
1641       scrollbar = Rectangle(spinnerUpLeft.x + spinnerUpLeft.width, bounds.y + scrollBarWidth + scrollBarPadding,
1642             bounds.width - spinnerUpLeft.width - spinnerDownRight.width - 2 * scrollBarWidth,
1643             bounds.height - 2 * (scrollBarWidth + scrollBarPadding));
1644       sliderSize = (sliderSize >= scrollbar.width) ? (scrollbar.width - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar
1645       slider = Rectangle(scrollbar.x + cast(int)((cast(float)(value - minValue) / range) * (scrollbar.width - sliderSize)),
1646             bounds.y + scrollBarWidth + sliderPadding, sliderSize, bounds.height - 2 * (scrollBarWidth + sliderPadding));
1647    }
1648 
1649    // Update control
1650    //--------------------------------------------------------------------
1651    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
1652       auto mousePoint = GetMousePosition();
1653 
1654       if (CheckCollisionPointRec(mousePoint, bounds)) {
1655          state = GUI_STATE_FOCUSED;
1656 
1657          // Handle mouse wheel
1658          immutable wheel = GetMouseWheelMove();
1659          if (wheel != 0)
1660             value += wheel;
1661 
1662          if (IsMouseButtonPressed(MouseButton.MOUSE_LEFT_BUTTON)) {
1663             if (CheckCollisionPointRec(mousePoint, spinnerUpLeft))
1664                value -= range / GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
1665             else if (CheckCollisionPointRec(mousePoint, spinnerDownRight))
1666                value += range / GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
1667 
1668             state = GUI_STATE_PRESSED;
1669          } else if (IsMouseButtonDown(MouseButton.MOUSE_LEFT_BUTTON)) {
1670             if (!isVertical) {
1671                Rectangle scrollArea = {
1672                   spinnerUpLeft.x + spinnerUpLeft.width, spinnerUpLeft.y, scrollbar.width, bounds.height - 2 * scrollBarWidth
1673                };
1674                if (CheckCollisionPointRec(mousePoint, scrollArea))
1675                   value = cast(int)((cast(float)(mousePoint.x - scrollArea.x - slider.width / 2) * range) / (scrollArea.width - slider
1676                         .width) + minValue);
1677             } else {
1678                Rectangle scrollArea = {
1679                   spinnerUpLeft.x, spinnerUpLeft.y + spinnerUpLeft.height, bounds.width - 2 * scrollBarWidth, scrollbar.height
1680                };
1681                if (CheckCollisionPointRec(mousePoint, scrollArea))
1682                   value = cast(int)((cast(float)(mousePoint.y - scrollArea.y - slider.height / 2) * range) / (
1683                         scrollArea.height - slider.height) + minValue);
1684             }
1685          }
1686       }
1687 
1688       // Normalize value
1689       value = value.clamp(minValue, maxValue);
1690    }
1691    //--------------------------------------------------------------------
1692 
1693    // Draw control
1694    //--------------------------------------------------------------------
1695    DrawRectangleRec(bounds, Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background
1696    DrawRectangleRec(scrollbar, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background
1697 
1698    DrawRectangleLinesEx(bounds, scrollBarWidth, Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state * 3)), guiAlpha));
1699 
1700    DrawRectangleRec(slider, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state * 3)), guiAlpha)); // Draw the slider bar
1701 
1702    // Draw arrows using lines
1703    immutable padding = (spinnerSize - GuiGetStyle(SCROLLBAR, ARROWS_SIZE)) / 2;
1704    const Vector2[] lineCoords = [ //coordinates for <     0,1,2
1705    {spinnerUpLeft.x + padding, spinnerUpLeft.y + spinnerSize / 2}, {
1706       spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + padding
1707    }, {
1708       spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + spinnerSize - padding
1709    }, //coordinates for >     3,4,5
1710    {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, {
1711       spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + spinnerSize / 2
1712    }, {
1713       spinnerDownRight.x + padding, spinnerDownRight.y + spinnerSize - padding
1714    }, //coordinates for ∧     6,7,8
1715    {spinnerUpLeft.x + spinnerSize / 2, spinnerUpLeft.y + padding}, {
1716       spinnerUpLeft.x + padding, spinnerUpLeft.y + spinnerSize - padding
1717    }, {
1718       spinnerUpLeft.x + spinnerSize - padding, spinnerUpLeft.y + spinnerSize - padding
1719    }, //coordinates for ∨     9,10,11
1720    {spinnerDownRight.x + padding, spinnerDownRight.y + padding}, {
1721       spinnerDownRight.x + spinnerSize / 2, spinnerDownRight.y + spinnerSize - padding
1722    }, {
1723       spinnerDownRight.x + spinnerSize - padding, spinnerDownRight.y + padding
1724    }];
1725 
1726    Color lineColor = Fade(GetColor(GuiGetStyle(BUTTON, TEXT + state * 3)), guiAlpha);
1727 
1728    if (GuiGetStyle(SCROLLBAR, SHOW_SPINNER_BUTTONS)) {
1729       if (isVertical) {
1730          // Draw ∧
1731          DrawLineEx(lineCoords[6], lineCoords[7], 3.0f, lineColor);
1732          DrawLineEx(lineCoords[6], lineCoords[8], 3.0f, lineColor);
1733 
1734          // Draw ∨
1735          DrawLineEx(lineCoords[9], lineCoords[10], 3.0f, lineColor);
1736          DrawLineEx(lineCoords[11], lineCoords[10], 3.0f, lineColor);
1737       } else {
1738          // Draw <
1739          DrawLineEx(lineCoords[0], lineCoords[1], 3.0f, lineColor);
1740          DrawLineEx(lineCoords[0], lineCoords[2], 3.0f, lineColor);
1741 
1742          // Draw >
1743          DrawLineEx(lineCoords[3], lineCoords[4], 3.0f, lineColor);
1744          DrawLineEx(lineCoords[5], lineCoords[4], 3.0f, lineColor);
1745       }
1746    }
1747    //--------------------------------------------------------------------
1748 
1749    return value;
1750 }
1751 
1752 /// List Element control, returns element state
1753 bool GuiListElement(Rectangle bounds, string text, bool active, bool editMode) {
1754    auto state = guiState;
1755    if (!guiLocked && editMode)
1756       state = GUI_STATE_NORMAL;
1757 
1758    // Update control
1759    //--------------------------------------------------------------------
1760    if (bounds.LeftClicked(state)) {
1761       active = !active;
1762    }
1763    //--------------------------------------------------------------------
1764 
1765    // Internal drawing function.
1766    void drawElement(in GuiControlProperty list, in GuiControlProperty border) {
1767       immutable viewColour = GuiGetStyle(LISTVIEW, list).GetColor.Fade(guiAlpha);
1768       immutable borderColour = GuiGetStyle(LISTVIEW, border).GetColor.Fade(guiAlpha);
1769       DrawRectangleRec(bounds, viewColour);
1770       DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), borderColour);
1771    }
1772 
1773    // Draw control
1774    //--------------------------------------------------------------------
1775    // Draw element rectangle
1776    switch (state) {
1777       case GUI_STATE_NORMAL:
1778          if (active) {
1779             drawElement(GuiControlProperty.BASE_COLOR_PRESSED, GuiControlProperty.BORDER_COLOR_PRESSED);
1780          }
1781          break;
1782       case GUI_STATE_FOCUSED:
1783          drawElement(GuiControlProperty.BASE_COLOR_FOCUSED, GuiControlProperty.BORDER_COLOR_FOCUSED);
1784          break;
1785       case GUI_STATE_PRESSED:
1786          drawElement(GuiControlProperty.BASE_COLOR_PRESSED, GuiControlProperty.BORDER_COLOR_PRESSED);
1787          break;
1788       case GUI_STATE_DISABLED:
1789          if (active) {
1790             drawElement(GuiControlProperty.BASE_COLOR_DISABLED, GuiControlProperty.BORDER_COLOR_NORMAL);
1791          }
1792          break;
1793       default:
1794          break;
1795    }
1796    immutable textBounds = GetTextBounds(DEFAULT, bounds);
1797    immutable textAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
1798    // Draw text depending on state
1799    if (state == GUI_STATE_NORMAL) {
1800       GuiDrawText(text, textBounds, textAlignment, Fade(GetColor(GuiGetStyle(LISTVIEW, active ? TEXT_COLOR_PRESSED
1801             : TEXT_COLOR_NORMAL)), guiAlpha));
1802    } else if (state == GUI_STATE_DISABLED) {
1803       GuiDrawText(text, textBounds, textAlignment, Fade(GetColor(GuiGetStyle(LISTVIEW, active ? TEXT_COLOR_NORMAL
1804             : TEXT_COLOR_DISABLED)), guiAlpha));
1805    } else {
1806       GuiDrawText(text, textBounds, textAlignment, Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + state * 3)), guiAlpha));
1807    }
1808    //--------------------------------------------------------------------
1809 
1810    return active;
1811 }
1812 
1813 /// List View control
1814 bool GuiListView(Rectangle bounds, string text, ref int active, ref int scrollIndex, bool editMode) {
1815    auto list = text.GuiTextSplit();
1816    int enabled;
1817    int focus;
1818    return GuiListViewEx(bounds, list, cast(int)list.length, enabled, active, focus, scrollIndex, editMode);
1819 }
1820 
1821 /// List View control extended parameters
1822 /// NOTE: Elements could be disabled individually and focused element could be obtained:
1823 ///  int *enabled defines an array with enabled elements inside the list
1824 ///  int *focus returns focused element (may be not pressed)
1825 bool GuiListViewEx(Rectangle bounds, string[] text, int count, ref int enabled, ref int active, ref int focus,
1826       ref int scrollIndex, bool editMode) {
1827    //FIXME: Seems to rely on null being a valid value for enabled and focus.
1828    import std.algorithm : clamp;
1829 
1830    auto state = guiState;
1831    bool pressed;
1832    int focusElement = -1;
1833    int startIndex = scrollIndex;
1834    bool useScrollBar = true;
1835    bool pressedKey;
1836    immutable elementsPadding = GuiGetStyle(LISTVIEW, ELEMENTS_PADDING);
1837    immutable elementsHeight = GuiGetStyle(LISTVIEW, ELEMENTS_HEIGHT);
1838    immutable scrollBarWidth = GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
1839    immutable borderWidth = GuiGetStyle(DEFAULT, BORDER_WIDTH);
1840    auto visibleElements = cast(int)(bounds.height / (elementsHeight + elementsPadding));
1841    startIndex = startIndex.clamp(0, count - visibleElements);
1842    int endIndex = startIndex + visibleElements;
1843 
1844    int auxActive = active;
1845 
1846    float barHeight = bounds.height;
1847    immutable minBarHeight = 10.0f;
1848 
1849    // Update control
1850    //--------------------------------------------------------------------
1851    // All the elements fit inside ListView and dont need scrollbar.
1852    if (visibleElements >= count) {
1853       useScrollBar = false;
1854       startIndex = 0;
1855       endIndex = count;
1856    }
1857 
1858    // Calculate position X and width to draw each element.
1859    auto posX = bounds.x + elementsPadding;
1860    auto elementWidth = bounds.width - 2 * elementsPadding - borderWidth;
1861 
1862    if (useScrollBar) {
1863       posX = GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE ? posX + scrollBarWidth : posX;
1864       elementWidth = bounds.width - scrollBarWidth - 2 * elementsPadding - borderWidth;
1865    }
1866 
1867    Rectangle scrollBarRect = {
1868       bounds.x + borderWidth, bounds.y + borderWidth, scrollBarWidth, bounds.height - 2 * borderWidth
1869    };
1870 
1871    if (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_RIGHT_SIDE)
1872       scrollBarRect.x = posX + elementWidth + elementsPadding;
1873 
1874    // Area without the scrollbar
1875    Rectangle viewArea = {posX, bounds.y + borderWidth, elementWidth, bounds.height - 2 * borderWidth};
1876 
1877    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
1878       auto mousePoint = GetMousePosition();
1879 
1880       if (editMode) {
1881          state = GUI_STATE_PRESSED;
1882          // Change active with keys
1883          if (IsKeyPressed(KeyboardKey.KEY_UP)) {
1884             if (auxActive > 0) {
1885                auxActive--;
1886                if ((useScrollBar) && (auxActive < startIndex)) {
1887                   startIndex--;
1888                }
1889             }
1890             pressedKey = true;
1891          } else if (IsKeyPressed(KeyboardKey.KEY_DOWN)) {
1892             if (auxActive < count - 1) {
1893                auxActive++;
1894                if ((useScrollBar) && (auxActive >= endIndex)) {
1895                   startIndex++;
1896                }
1897             }
1898 
1899             pressedKey = true;
1900          }
1901 
1902          if (useScrollBar) {
1903             endIndex = startIndex + visibleElements;
1904             if (CheckCollisionPointRec(mousePoint, viewArea)) {
1905                immutable wheel = GetMouseWheelMove();
1906                if (wheel < 0 && endIndex < count) {
1907                   startIndex -= wheel;
1908                } else if (wheel > 0 && startIndex > 0) {
1909                   startIndex -= wheel;
1910                }
1911             }
1912 
1913             if (pressedKey) {
1914                pressedKey = false;
1915                if ((auxActive < startIndex) || (auxActive >= endIndex)) {
1916                   startIndex = auxActive;
1917                }
1918             }
1919 
1920             if (startIndex < 0) {
1921                startIndex = 0;
1922             } else if (startIndex > (count - (endIndex - startIndex))) {
1923                startIndex = count - (endIndex - startIndex);
1924             }
1925 
1926             endIndex = startIndex + visibleElements;
1927 
1928             if (endIndex > count) {
1929                endIndex = count;
1930             }
1931          }
1932       }
1933 
1934       if (!editMode) {
1935          if (CheckCollisionPointRec(mousePoint, viewArea)) {
1936             state = GUI_STATE_FOCUSED;
1937             if (IsMouseButtonPressed(0)) {
1938                pressed = true;
1939             }
1940 
1941             startIndex -= GetMouseWheelMove();
1942 
1943             if (startIndex < 0) {
1944                startIndex = 0;
1945             } else if (startIndex > (count - (endIndex - startIndex))) {
1946                startIndex = count - (endIndex - startIndex);
1947             }
1948 
1949             pressed = true;
1950          }
1951       } else {
1952          if (!CheckCollisionPointRec(mousePoint, viewArea)) {
1953             if (IsMouseButtonPressed(0) || (GetMouseWheelMove() != 0)) {
1954                pressed = true;
1955             }
1956          }
1957       }
1958 
1959       // Get focused element
1960       foreach (i; startIndex .. endIndex) {
1961          if (CheckCollisionPointRec(mousePoint, Rectangle(posX,
1962                bounds.y + elementsPadding + borderWidth + (i - startIndex) * (elementsHeight + elementsPadding),
1963                elementWidth, elementsHeight))) {
1964             focusElement = i;
1965          }
1966       }
1967    }
1968 
1969    immutable slider = GuiGetStyle(SCROLLBAR, SLIDER_SIZE); // Save default slider size
1970 
1971    // Calculate percentage of visible elements and apply same percentage to scrollbar
1972    if (useScrollBar) {
1973       immutable percentVisible = (endIndex - startIndex) * 100.0f / count;
1974       barHeight *= percentVisible / 100;
1975       barHeight = barHeight.clamp(minBarHeight, bounds.height);
1976 
1977       GuiSetStyle(SCROLLBAR, SLIDER_SIZE, cast(int)barHeight); // Change slider size
1978    }
1979 
1980    // Draw control
1981    DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background
1982 
1983    // Draw scrollBar
1984    if (useScrollBar) {
1985       immutable scrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
1986       GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleElements); // Hack to make the spinner buttons work
1987 
1988       int index = scrollIndex != 0 ? scrollIndex : startIndex;
1989       index = GuiScrollBar(scrollBarRect, index, 0, count - visibleElements);
1990 
1991       GuiSetStyle(SCROLLBAR, SCROLL_SPEED, scrollSpeed); // Reset scroll speed to default
1992       GuiSetStyle(SCROLLBAR, SLIDER_SIZE, slider); // Reset slider size to default
1993 
1994       // FIXME: Quick hack to make this thing work, think of a better way
1995       if (CheckCollisionPointRec(GetMousePosition(), scrollBarRect) && IsMouseButtonDown(MouseButton.MOUSE_LEFT_BUTTON)) {
1996          startIndex = index;
1997          startIndex = startIndex.clamp(0, (count - (endIndex - startIndex)));
1998          endIndex = startIndex + visibleElements;
1999          if (endIndex > count) {
2000             endIndex = count;
2001          }
2002       }
2003    }
2004 
2005    Rectangle elementRect(int i) {
2006       //FIXME: elementWidth-1 is a temporary fix.  Must be a rounding error from truncation.
2007       return Rectangle(posX, bounds.y + elementsPadding + borderWidth + (i - startIndex) * (elementsHeight + elementsPadding),
2008             elementWidth - 1, elementsHeight);
2009    }
2010 
2011    DrawRectangleLinesEx(bounds, borderWidth, Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state * 3)), guiAlpha));
2012 
2013    // Draw ListView states
2014    switch (state) {
2015       case GUI_STATE_NORMAL:
2016          foreach (i; startIndex .. endIndex) {
2017             if (enabled == 0) {
2018                GuiDisable();
2019                GuiListElement(elementRect(i), text[i], false, false);
2020                GuiEnable();
2021             } else if (i == auxActive) {
2022                GuiDisable();
2023                GuiListElement(elementRect(i), text[i], true, false);
2024                GuiEnable();
2025             } else
2026                GuiListElement(elementRect(i), text[i], false, false);
2027          }
2028          break;
2029       case GUI_STATE_FOCUSED:
2030          foreach (i; startIndex .. endIndex) {
2031             if (enabled == 0) {
2032                GuiDisable();
2033                GuiListElement(elementRect(i), text[i], false, false);
2034                GuiEnable();
2035             } else if (i == auxActive) {
2036                GuiListElement(elementRect(i), text[i], true, false);
2037             } else {
2038                GuiListElement(elementRect(i), text[i], false, false);
2039             }
2040          }
2041          break;
2042       case GUI_STATE_PRESSED:
2043          foreach (i; startIndex .. endIndex) {
2044             if (enabled == 0) {
2045                GuiDisable();
2046                GuiListElement(elementRect(i), text[i], false, false);
2047                GuiEnable();
2048             } else if ((i == auxActive) && editMode) {
2049                if (GuiListElement(elementRect(i), text[i], true, true) == false)
2050                   auxActive = -1;
2051             } else {
2052                if (GuiListElement(elementRect(i), text[i], false, true) == true)
2053                   auxActive = i;
2054             }
2055          }
2056          break;
2057       case GUI_STATE_DISABLED:
2058          foreach (i; startIndex .. endIndex) {
2059             if (i == auxActive)
2060                GuiListElement(elementRect(i), text[i], true, false);
2061             else
2062                GuiListElement(elementRect(i), text[i], false, false);
2063          }
2064          break;
2065       default:
2066          break;
2067    }
2068    //--------------------------------------------------------------------
2069 
2070    //FIXME: Null stuff?
2071    scrollIndex = startIndex;
2072    focus = focusElement;
2073    active = auxActive;
2074 
2075    return pressed;
2076 }
2077 
2078 // Color Panel control
2079 
2080 // HSV: Saturation
2081 // HSV: Value
2082 
2083 // Update control
2084 //--------------------------------------------------------------------
2085 
2086 // Calculate color from picker
2087 
2088 // Get normalized value on x
2089 // Get normalized value on y
2090 
2091 // NOTE: Vector3ToColor() only available on raylib 1.8.1
2092 
2093 //--------------------------------------------------------------------
2094 
2095 // Draw control
2096 //--------------------------------------------------------------------
2097 
2098 // Draw color picker: selector
2099 
2100 //--------------------------------------------------------------------
2101 Color GuiColorPanel(Rectangle bounds, Color color);
2102 
2103 // Color Bar Alpha control
2104 // NOTE: Returns alpha value normalized [0..1]
2105 
2106 // Update control
2107 //--------------------------------------------------------------------
2108 
2109 //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2;
2110 
2111 //--------------------------------------------------------------------
2112 
2113 // Draw control
2114 //--------------------------------------------------------------------
2115 // Draw alpha bar: checked background
2116 
2117 //--------------------------------------------------------------------
2118 float GuiColorBarAlpha(Rectangle bounds, float alpha) {
2119    // FIX:
2120    return 0;
2121 }
2122 
2123 enum COLORBARALPHA_CHECKED_SIZE = 10;
2124 
2125 // Color Bar Hue control
2126 // NOTE: Returns hue value normalized [0..1]
2127 
2128 // Update control
2129 //--------------------------------------------------------------------
2130 
2131 /*if (IsKeyDown(KeyboardKey.KEY_UP))
2132   {
2133   hue -= 2.0f;
2134   if (hue <= 0.0f) hue = 0.0f;
2135   }
2136   else if (IsKeyDown(KeyboardKey.KEY_DOWN))
2137   {
2138   hue += 2.0f;
2139   if (hue >= 360.0f) hue = 360.0f;
2140   }*/
2141 
2142 //--------------------------------------------------------------------
2143 
2144 // Draw control
2145 //--------------------------------------------------------------------
2146 
2147 // Draw hue bar:color bars
2148 
2149 // Draw hue bar: selector
2150 
2151 //--------------------------------------------------------------------
2152 float GuiColorBarHue(Rectangle bounds, float hue) {
2153    // FIX:
2154    return 0;
2155 }
2156 
2157 // TODO: Color GuiColorBarSat() [WHITE->color]
2158 // TODO: Color GuiColorBarValue() [BLACK->color], HSV / HSL
2159 // TODO: float GuiColorBarLuminance() [BLACK->WHITE]
2160 
2161 // Color Picker control
2162 // NOTE: It's divided in multiple controls:
2163 //      Color GuiColorPanel() - Color select panel
2164 //      float GuiColorBarAlpha(Rectangle bounds, float alpha)
2165 //      float GuiColorBarHue(Rectangle bounds, float value)
2166 // NOTE: bounds define GuiColorPanel() size
2167 
2168 //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
2169 
2170 //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f);
2171 Color GuiColorPicker(Rectangle bounds, Color color) {
2172    // FIX:
2173    return WHITE;
2174 }
2175 
2176 /// Message Box control
2177 int GuiMessageBox(Rectangle bounds, string windowTitle, string message, string buttons) {
2178    import std..string : toStringz;
2179 
2180    enum MESSAGEBOX_BUTTON_HEIGHT = 24;
2181    enum MESSAGEBOX_BUTTON_PADDING = 10;
2182    enum WINDOW_STATUSBAR_HEIGHT = 24;
2183    int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button
2184    const buttonsText = buttons.GuiTextSplit();
2185    immutable(int) buttonsCount = cast(int)buttonsText.length;
2186 
2187    immutable textSize = MeasureTextEx(guiFont, message.toStringz, GuiGetStyle(DEFAULT, TEXT_SIZE), 1);
2188 
2189    Rectangle textBounds = {
2190       bounds.x + bounds.width / 2 - textSize.x / 2,
2191          bounds.y + WINDOW_STATUSBAR_HEIGHT + (bounds.height - WINDOW_STATUSBAR_HEIGHT) / 4 - textSize.y / 2, textSize.x, textSize.y
2192    };
2193 
2194    Rectangle buttonBounds = {
2195       bounds.x + MESSAGEBOX_BUTTON_PADDING, bounds.y + bounds.height / 2 + bounds.height / 4 - MESSAGEBOX_BUTTON_HEIGHT / 2,
2196          (bounds.width - MESSAGEBOX_BUTTON_PADDING * (buttonsCount + 1)) / buttonsCount, MESSAGEBOX_BUTTON_HEIGHT
2197    };
2198 
2199    // Draw control
2200    if (GuiWindowBox(bounds, windowTitle)) {
2201       clicked = 0;
2202    }
2203 
2204    int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
2205    GuiSetStyle(LABEL, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
2206    GuiLabel(textBounds, message);
2207    GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
2208 
2209    prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
2210    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, GUI_TEXT_ALIGN_CENTER);
2211 
2212    foreach (int i; 0 .. buttonsCount) {
2213       if (GuiButton(buttonBounds, buttonsText[i])) {
2214          clicked = i + 1;
2215       }
2216       buttonBounds.x += (buttonBounds.width + MESSAGEBOX_BUTTON_PADDING);
2217    }
2218 
2219    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment);
2220    //--------------------------------------------------------------------
2221 
2222    return clicked;
2223 }
2224 
2225 /// Text Input Box control, ask for text
2226 int GuiTextInputBox(Rectangle bounds, string windowTitle, string message, string text, string buttons) {
2227    int btnIndex = -1;
2228    //TODO
2229    return btnIndex;
2230 }
2231 
2232 /// Grid control
2233 /// NOTE: Returns grid mouse-hover selected cell
2234 /// About drawing lines at subpixel spacing, simple put, not easy solution:
2235 /// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
2236 Vector2 GuiGrid(Rectangle bounds, float spacing, int subdivs) {
2237    enum GRID_COLOR_ALPHA = 0.15f;
2238    auto state = guiState;
2239    auto mousePoint = GetMousePosition();
2240    Vector2 currentCell = {-1, -1};
2241 
2242    int linesV = (cast(int)(bounds.width / spacing) + 1) * subdivs;
2243    int linesH = (cast(int)(bounds.height / spacing) + 1) * subdivs;
2244 
2245    // Update control
2246    if ((state != GUI_STATE_DISABLED) && !guiLocked) {
2247       if (CheckCollisionPointRec(mousePoint, bounds)) {
2248          currentCell.x = cast(int)((mousePoint.x - bounds.x) / spacing);
2249          currentCell.y = cast(int)((mousePoint.y - bounds.y) / spacing);
2250       }
2251    }
2252 
2253    // Draw control
2254    switch (state) {
2255       case GUI_STATE_NORMAL:
2256          // Draw vertical grid lines
2257          foreach (i; 0 .. linesV) {
2258             DrawRectangleRec(Rectangle(bounds.x + spacing * i, bounds.y, 1, bounds.height), ((i % subdivs) == 0)
2259                   ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA * 4)
2260                   : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA));
2261          }
2262 
2263          // Draw horizontal grid lines
2264          foreach (i; 0 .. linesH) {
2265             DrawRectangleRec(Rectangle(bounds.x, bounds.y + spacing * i, bounds.width, 1), ((i % subdivs) == 0)
2266                   ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA * 4)
2267                   : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), GRID_COLOR_ALPHA));
2268          }
2269 
2270          break;
2271       default:
2272          break;
2273    }
2274 
2275    return currentCell;
2276 }
2277 
2278 //----------------------------------------------------------------------------------
2279 // Styles loading functions
2280 //----------------------------------------------------------------------------------
2281 
2282 /// Load raygui style file (.rgs)
2283 /// TEXT ONLY FOR NOW.
2284 void GuiLoadStyle(string fileName) {
2285    import std.regex : matchFirst, ctRegex;
2286    import std.typecons : Tuple;
2287    import std.conv : to;
2288    import std.stdio : File;
2289    import std..string : toStringz;
2290 
2291    alias Style = Tuple!(string, "type", int, "control", int, "property", string, "value");
2292    auto controlRegex = ctRegex!(`^(?P<type>p) (?P<control>\d+) (?P<property>\d+) \b0x(?P<value>[0-9a-fA-F]{8})\b`);
2293    auto fontRegex = ctRegex!(`^(?P<type>f) (?P<control>\d+) (?P<property>\d+) (?P<value>.+(.ttf|.otf))`);
2294    File styles;
2295    try {
2296       styles = File(fileName, "rt");
2297       foreach (style; styles.byLine) {
2298          auto match = style.matchFirst(controlRegex);
2299          if (match.empty) {
2300             match = style.matchFirst(fontRegex);
2301          }
2302          if (!match.empty) {
2303             immutable s = Style(match["type"].to!string, match["control"].to!int, match["property"].to!int, match["value"].to!string);
2304             switch (s.type) {
2305                case "p":
2306                   int colour = s.value.to!uint(16);
2307                   if (s.control == 0) {
2308                      // If a DEFAULT property is loaded, it is propagated to all controls,
2309                      // NOTE: All DEFAULT properties should be defined first in the file
2310                      GuiSetStyle(0, s.property, colour);
2311                      if (s.property < NUM_PROPS_DEFAULT) {
2312                         foreach (i; 1 .. NUM_CONTROLS)
2313                            GuiSetStyle(i, s.property, colour);
2314                      }
2315                   } else {
2316                      GuiSetStyle(s.control, s.property, colour);
2317                   }
2318                   break;
2319                case "f":
2320                   auto fontFileName = s.value.toStringz;
2321                   Font font = "%s/%s".toStringz.TextFormat(fileName.toStringz.GetDirectoryPath(), fontFileName)
2322                      .LoadFontEx(s.control, null, s.property);
2323                   if ((font.texture.id > 0) && (font.charsCount > 0)) {
2324                      GuiFont(font);
2325                      GuiSetStyle(DEFAULT, TEXT_SIZE, s.control);
2326                      GuiSetStyle(DEFAULT, TEXT_SPACING, s.property);
2327                   }
2328                   break;
2329                default:
2330                   break;
2331             }
2332          }
2333       }
2334    }  //Catch any exception.  Could tighten this up.
2335    catch (Exception) {
2336    }
2337    scope (exit) {
2338       styles.close();
2339    }
2340 }
2341 
2342 /// Load style from a palette values array
2343 void GuiLoadStyleProps(in int[] props, int count) {
2344    int completeSets = count / (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED);
2345    int uncompleteSetProps = count % (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED);
2346 
2347    // Load style palette values from array (complete property sets)
2348    foreach (i; 0 .. completeSets) {
2349       // TODO: This code needs review
2350       foreach (j; 0 .. (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED)) {
2351          GuiSetStyle(i, j, props[i]);
2352       }
2353    }
2354 
2355    // Load style palette values from array (uncomplete property set)
2356    foreach (k; 0 .. uncompleteSetProps) {
2357       GuiSetStyle(completeSets, k, props[completeSets * (NUM_PROPS_DEFAULT + NUM_PROPS_EXTENDED) + k]);
2358    }
2359 }
2360 
2361 /// Load style default over global style
2362 void GuiLoadStyleDefault() {
2363    // We set this variable first to avoid cyclic function calls
2364    // when calling GuiSetStyle() and GuiGetStyle()
2365    guiStyleLoaded = true;
2366    // Initialize default LIGHT style property values
2367    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BORDER_COLOR_NORMAL, 0x838383ff);
2368    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BASE_COLOR_NORMAL, 0xc9c9c9ff);
2369    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_COLOR_NORMAL, 0x686868ff);
2370    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BORDER_COLOR_FOCUSED, 0x5bb2d9ff);
2371    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BASE_COLOR_FOCUSED, 0xc9effeff);
2372    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_COLOR_FOCUSED, 0x6c9bbcff);
2373    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BORDER_COLOR_PRESSED, 0x0492c7ff);
2374    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BASE_COLOR_PRESSED, 0x97e8ffff);
2375    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_COLOR_PRESSED, 0x368bafff);
2376    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BORDER_COLOR_DISABLED, 0xb5c1c2ff);
2377    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BASE_COLOR_DISABLED, 0xe6e9e9ff);
2378    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_COLOR_DISABLED, 0xaeb7b8ff);
2379    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.BORDER_WIDTH, 1);
2380    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.INNER_PADDING, 1);
2381    GuiSetStyle(GuiControl.DEFAULT, GuiControlProperty.TEXT_ALIGNMENT, GuiTextAlignment.GUI_TEXT_ALIGN_CENTER);
2382    // Populate all controls with default style
2383    GuiUpdateStyleComplete();
2384    //Initialise default font.
2385    guiFont = GetFontDefault();
2386    // Initialize extended property values
2387    // NOTE: By default, extended property values are initialized to 0
2388    GuiSetStyle(GuiControl.DEFAULT, GuiDefaultProperty.TEXT_SIZE, 10);
2389    GuiSetStyle(GuiControl.DEFAULT, GuiDefaultProperty.TEXT_SPACING, 1);
2390    GuiSetStyle(GuiControl.DEFAULT, GuiDefaultProperty.LINE_COLOR, 0x90abb5ff); // GuiControl.DEFAULT specific property
2391    GuiSetStyle(GuiControl.DEFAULT, GuiDefaultProperty.BACKGROUND_COLOR, 0xf5f5f5ff); // GuiControl.DEFAULT specific property
2392    GuiSetStyle(GuiControl.LABEL, GuiControlProperty.TEXT_ALIGNMENT, GuiTextAlignment.GUI_TEXT_ALIGN_LEFT);
2393    GuiSetStyle(GuiControl.BUTTON, GuiControlProperty.BORDER_WIDTH, 2);
2394    GuiSetStyle(GuiControl.BUTTON, GuiControlProperty.INNER_PADDING, 4);
2395    GuiSetStyle(GuiControl.TOGGLE, GuiToggleProperty.GROUP_PADDING, 2);
2396    GuiSetStyle(GuiControl.SLIDER, GuiSliderProperty.SLIDER_WIDTH, 15);
2397    GuiSetStyle(GuiControl.SLIDER, GuiSliderProperty.TEXT_PADDING, 5);
2398    GuiSetStyle(GuiControl.CHECKBOX, GuiCheckBoxProperty.CHECK_TEXT_PADDING, 5);
2399    GuiSetStyle(GuiControl.COMBOBOX, GuiComboBoxProperty.SELECTOR_WIDTH, 30);
2400    GuiSetStyle(GuiControl.COMBOBOX, GuiComboBoxProperty.SELECTOR_PADDING, 2);
2401    GuiSetStyle(GuiControl.DROPDOWNBOX, GuiDropdownBoxProperty.ARROW_RIGHT_PADDING, 16);
2402    GuiSetStyle(GuiControl.TEXTBOX, GuiControlProperty.INNER_PADDING, 4);
2403    GuiSetStyle(GuiControl.TEXTBOX, GuiControlProperty.TEXT_ALIGNMENT, GuiTextAlignment.GUI_TEXT_ALIGN_LEFT);
2404    GuiSetStyle(GuiControl.TEXTBOX, GuiTextBoxProperty.MULTILINE_PADDING, 5);
2405    GuiSetStyle(GuiControl.TEXTBOX, GuiTextBoxProperty.COLOR_SELECTED_FG, 0xf0fffeff);
2406    GuiSetStyle(GuiControl.TEXTBOX, GuiTextBoxProperty.COLOR_SELECTED_BG, 0x839affe0);
2407    GuiSetStyle(GuiControl.VALUEBOX, GuiControlProperty.TEXT_ALIGNMENT, GuiTextAlignment.GUI_TEXT_ALIGN_CENTER);
2408    GuiSetStyle(GuiControl.SPINNER, GuiSpinnerProperty.SELECT_BUTTON_WIDTH, 20);
2409    GuiSetStyle(GuiControl.SPINNER, GuiSpinnerProperty.SELECT_BUTTON_PADDING, 2);
2410    GuiSetStyle(GuiControl.SPINNER, GuiSpinnerProperty.SELECT_BUTTON_BORDER_WIDTH, 1);
2411    GuiSetStyle(GuiControl.SCROLLBAR, GuiControlProperty.BORDER_WIDTH, 0);
2412    GuiSetStyle(GuiControl.SCROLLBAR, GuiScrollBarProperty.SHOW_SPINNER_BUTTONS, 0);
2413    GuiSetStyle(GuiControl.SCROLLBAR, GuiControlProperty.INNER_PADDING, 0);
2414    GuiSetStyle(GuiControl.SCROLLBAR, GuiScrollBarProperty.ARROWS_SIZE, 6);
2415    GuiSetStyle(GuiControl.SCROLLBAR, GuiScrollBarProperty.SLIDER_PADDING, 0);
2416    GuiSetStyle(GuiControl.SCROLLBAR, GuiScrollBarProperty.SLIDER_SIZE, 16);
2417    GuiSetStyle(GuiControl.SCROLLBAR, GuiScrollBarProperty.SCROLL_SPEED, 10);
2418    GuiSetStyle(GuiControl.LISTVIEW, GuiListViewProperty.ELEMENTS_HEIGHT, 0x1e);
2419    GuiSetStyle(GuiControl.LISTVIEW, GuiListViewProperty.ELEMENTS_PADDING, 2);
2420    GuiSetStyle(GuiControl.LISTVIEW, GuiListViewProperty.SCROLLBAR_WIDTH, 10);
2421    GuiSetStyle(GuiControl.LISTVIEW, GuiListViewProperty.SCROLLBAR_SIDE, GuiScrollBarSide.SCROLLBAR_RIGHT_SIDE);
2422    GuiSetStyle(GuiControl.COLORPICKER, GuiColorPickerProperty.COLOR_SELECTOR_SIZE, 6);
2423    GuiSetStyle(GuiControl.COLORPICKER, GuiColorPickerProperty.BAR_WIDTH, 0x14);
2424    GuiSetStyle(GuiControl.COLORPICKER, GuiColorPickerProperty.BAR_PADDING, 0xa);
2425    GuiSetStyle(GuiControl.COLORPICKER, GuiColorPickerProperty.BAR_SELECTOR_HEIGHT, 6);
2426    GuiSetStyle(GuiControl.COLORPICKER, GuiColorPickerProperty.BAR_SELECTOR_PADDING, 2);
2427 }
2428 
2429 /// Updates controls style with default values
2430 void GuiUpdateStyleComplete() {
2431    // Populate all controls with default style
2432    // NOTE: Extended style properties are ignored
2433    foreach (i; 1 .. NUM_CONTROLS) {
2434       foreach (j; 0 .. NUM_PROPS_DEFAULT) {
2435          GuiSetStyle(i, j, GuiGetStyle(GuiControl.DEFAULT, j));
2436       }
2437    }
2438 }
2439 
2440 /// Get text with icon id prepended
2441 /// NOTE: Useful to add icons by name id (enum) instead of
2442 /// a number that can change between ricon versions
2443 string GuiIconText(int iconId, string text) {
2444    import std.format : format;
2445    return "#%03d#%s".format(iconId, text);
2446 }
2447 
2448 //----------------------------------------------------------------------------------
2449 // Module specific Functions Definition
2450 //----------------------------------------------------------------------------------
2451 
2452 /// Split controls text into multiple strings
2453 /// Also check for multiple columns (required by GuiToggleGroup())
2454 private string[] GuiTextSplit(string text) {
2455    //TODO: Multiple columns.
2456    import std.regex : splitter, regex;
2457    import std.typecons : No;
2458    import std.array : array;
2459    import std.algorithm : min;
2460 
2461    enum MAX_SUBSTRINGS_COUNT = 64;
2462    static pattern = regex(`([;\n])`);
2463    auto result = text.splitter!(No.keepSeparators)(pattern).array;
2464    return result[0 .. MAX_SUBSTRINGS_COUNT.min(result.length)];
2465 }
2466 
2467 // Convert color data from RGB to HSV
2468 // NOTE: Color data should be passed normalized
2469 
2470 // Value
2471 
2472 // Undefined, maybe NAN?
2473 
2474 // NOTE: If max is 0, this divide would cause a crash
2475 // Saturation
2476 
2477 // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
2478 
2479 // Undefined, maybe NAN?
2480 
2481 // NOTE: Comparing float values could not work properly
2482 // Between yellow & magenta
2483 
2484 // Between cyan & yellow
2485 // Between magenta & cyan
2486 
2487 // Convert to degrees
2488 Vector3 ConvertRGBtoHSV(Vector3 rgb);
2489 
2490 // Convert color data from HSV to RGB
2491 // NOTE: Color data should be passed normalized
2492 
2493 // NOTE: Comparing float values could not work properly
2494 Vector3 ConvertHSVtoRGB(Vector3 hsv);
2495 
2496 // Returns a Color struct from hexadecimal value
2497 
2498 // Returns hexadecimal value for a Color
2499 
2500 // Check if point is inside rectangle
2501 
2502 // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
2503 
2504 // Formatting of text with variables to 'embed'
2505 
2506 // RAYGUI_STANDALONE
2507 
2508 // RAYGUI_IMPLEMENTATION