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