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