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