1 /++ 2 Some widgets that are included in the package. 3 4 5 +/ 6 module qui.widgets; 7 8 import qui.qui; 9 import qui.misc; 10 import qui.lists; 11 12 ///Displays some text 13 /// 14 ///And it can't handle new-line characters 15 /// 16 ///Name in theme: 'text-label'; 17 class TextLabelWidget : QWidget{ 18 private: 19 RGBColor textColor, bgColor; 20 public: 21 this(string wCaption = ""){ 22 widgetName = "text-label"; 23 widgetCaption = wCaption; 24 } 25 26 override void updateColors(){ 27 if (&widgetTheme && widgetTheme.hasColors(name,["background","text"])){ 28 textColor = widgetTheme.getColor(name, "text"); 29 bgColor = widgetTheme.getColor(name, "background"); 30 }else{ 31 //use default values 32 textColor = hexToColor("00FF00"); 33 bgColor = hexToColor("000000"); 34 } 35 } 36 37 override bool update(ref Matrix display){ 38 if (needsUpdate){ 39 needsUpdate = false; 40 //redraw text 41 display.write(cast(char[])widgetCaption, textColor, bgColor); 42 return true; 43 }else{ 44 return false; 45 } 46 } 47 } 48 49 /// Displays a left-to-right progress bar. 50 /// 51 /// If caption is set, it is displayed in the middle of the widget 52 /// 53 /// Name in theme: 'progressbar'; 54 class ProgressbarWidget : QWidget{ 55 private: 56 uinteger max, done; 57 RGBColor bgColor, barColor, textColor; 58 void writeBarLine(ref Matrix display, uinteger filled, char[] bar){ 59 display.write(bar[0 .. filled], textColor, barColor); 60 display.write(bar[filled .. bar.length], textColor, bgColor); 61 } 62 public: 63 this(uinteger totalAmount = 100, uinteger complete = 0){ 64 widgetName = "progressbar"; 65 widgetCaption = null; 66 max = totalAmount; 67 done = complete; 68 } 69 70 override void updateColors(){ 71 needsUpdate = true; 72 if (&widgetTheme && widgetTheme.hasColors(name, ["background", "bar", "text"])){ 73 bgColor = widgetTheme.getColor(name, "background"); 74 barColor = widgetTheme.getColor(name, "bar"); 75 textColor = widgetTheme.getColor(name, "text"); 76 }else{ 77 bgColor = hexToColor("A6A6A6"); 78 barColor = hexToColor("00FF00"); 79 textColor = hexToColor("000000"); 80 } 81 if (forceUpdate !is null){ 82 forceUpdate(); 83 } 84 } 85 86 override bool update(ref Matrix display){ 87 bool r = false; 88 if (needsUpdate){ 89 needsUpdate = false; 90 r = true; 91 uinteger filled = ratioToRaw(done, max, widgetSize.width); 92 char[] bar; 93 bar.length = widgetSize.width; 94 bar[0 .. bar.length] = ' '; 95 if (widgetCaption != null){ 96 //write the caption too! 97 uinteger middle = widgetSize.height/2; 98 for (uinteger i = 0; i < widgetSize.height; i++){ 99 if (i == middle){ 100 bar = centerAlignText(cast(char[])caption, widgetSize.width); 101 writeBarLine(display, filled, bar); 102 bar[0 .. bar.length] = ' '; 103 continue; 104 }else{ 105 writeBarLine(display, filled, bar); 106 } 107 } 108 }else{ 109 for (uinteger i = 0; i < widgetSize.height; i++){ 110 writeBarLine(display, filled, bar); 111 } 112 } 113 } 114 return r; 115 } 116 /// The 'total', or the max-progress. getter 117 @property uinteger total(){ 118 return max; 119 } 120 /// The 'total' or the max-progress. setter 121 @property uinteger total(uinteger newTotal){ 122 needsUpdate = true; 123 max = newTotal; 124 if (forceUpdate !is null){ 125 forceUpdate(); 126 } 127 return max; 128 } 129 /// the amount of progress. getter 130 @property uinteger progress(){ 131 return done; 132 } 133 /// the amount of progress. setter 134 @property uinteger progress(uinteger newProgress){ 135 needsUpdate = true; 136 done = newProgress; 137 if (forceUpdate !is null){ 138 forceUpdate(); 139 } 140 return done; 141 } 142 } 143 144 /// To get single-line input from keyboard 145 /// 146 /// Name in theme: 'editline'; 147 class EditLineWidget : QWidget{ 148 private: 149 char[] inputText; 150 uinteger cursorX; 151 uinteger scrollX = 0;//amount of chars that won't be displayed because of not enough space 152 RGBColor bgColor, textColor, captionTextColor, captionBgColor; 153 public: 154 this(string wCaption = "", string inputTxt = ""){ 155 widgetName = "editLine"; 156 inputText = cast(char[])inputTxt; 157 widgetCaption = wCaption; 158 //specify min/max 159 widgetSize.minWidth = 1; 160 widgetSize.minHeight = 1; 161 widgetSize.maxHeight = 1; 162 } 163 override void updateColors(){ 164 needsUpdate = true; 165 if (&widgetTheme && widgetTheme.hasColors(name, ["background", "caption-text", "text"])){ 166 bgColor = widgetTheme.getColor(name, "background"); 167 textColor = widgetTheme.getColor(name, "text"); 168 captionTextColor = widgetTheme.getColor(name, "captionText"); 169 captionBgColor = widgetTheme.getColor(name, "captionBackground"); 170 }else{ 171 bgColor = hexToColor("404040"); 172 textColor = hexToColor("00FF00"); 173 captionTextColor = textColor; 174 captionBgColor = hexToColor("000000"); 175 } 176 if (forceUpdate !is null){ 177 forceUpdate(); 178 } 179 } 180 override bool update(ref Matrix display){ 181 bool r = false; 182 if (needsUpdate){ 183 needsUpdate = false; 184 r = true; 185 //make sure there's enough space 186 if (display.width > widgetCaption.length){ 187 //draw the caption 188 display.write(cast(char[])widgetCaption, captionTextColor, captionBgColor); 189 //draw the inputText 190 uinteger width = display.width - widgetCaption.length; 191 if (width >= inputText.length){ 192 //draw it as it is 193 display.write(inputText, textColor, bgColor); 194 }else{ 195 //draw scrolled 196 if (scrollX + width > inputText.length){ 197 display.write(inputText[scrollX .. inputText.length], textColor, bgColor); 198 }else{ 199 display.write(inputText[scrollX .. scrollX + width], textColor, bgColor); 200 } 201 } 202 //fill the left with bgColor 203 if (widgetCaption.length + inputText.length < widgetSize.width){ 204 char[] tmp; 205 tmp.length = widgetSize.width - (inputText.length + widgetCaption.length); 206 tmp[0 .. tmp.length] = ' '; 207 display.write(tmp, textColor, bgColor); 208 } 209 //set cursor pos, if can 210 if (cursorPos !is null){ 211 cursorPos(widgetPosition.x + widgetCaption.length + (cursorX - scrollX), widgetPosition.y); 212 } 213 }else{ 214 widgetShow = false; 215 r = false; 216 } 217 } 218 return r; 219 } 220 221 override void mouseEvent(MouseClick mouse){ 222 super.mouseEvent(mouse); 223 if (mouse.mouseButton == mouse.Button.Left){ 224 needsUpdate = true; 225 //move cursor to that pos 226 uinteger tmp = widgetPosition.x + widgetCaption.length; 227 if (mouse.x > tmp && mouse.x < tmp + inputText.length){ 228 cursorX = mouse.x - (widgetPosition.x + widgetCaption.length + scrollX); 229 } 230 } 231 } 232 override void keyboardEvent(KeyPress key){ 233 super.keyboardEvent(key); 234 if (key.isChar){ 235 needsUpdate = true; 236 //insert that key 237 if (key.key != '\b' && key.key != '\n'){ 238 if (cursorX == inputText.length){ 239 //insert at end 240 inputText ~= cast(char)key.key; 241 }else{ 242 inputText = inputText.insertArray([cast(char)key.key], cursorX); 243 } 244 cursorX ++; 245 }else if (key.key == '\b'){ 246 //backspace 247 if (cursorX > 0){ 248 if (cursorX == inputText.length){ 249 inputText.length --; 250 }else{ 251 inputText = inputText.deleteArray(cursorX-1); 252 } 253 cursorX --; 254 } 255 } 256 //check if has to modify scrollX 257 if (widgetSize.width < widgetCaption.length + inputText.length){ 258 scrollX = (widgetCaption.length + inputText.length) - widgetSize.width; 259 }else if (scrollX > 0){ 260 scrollX = 0; 261 } 262 }else{ 263 if (key.key == key.NonCharKey.LeftArrow || key.key == key.NonCharKey.UpArrow){ 264 if (cursorX > 0){ 265 needsUpdate = true; 266 cursorX --; 267 if (scrollX > cursorX){ 268 scrollX = cursorX; 269 } 270 } 271 }else if (key.key == key.NonCharKey.RightArrow || key.key == key.NonCharKey.DownArrow){ 272 if (cursorX < inputText.length){ 273 needsUpdate = true; 274 cursorX ++; 275 if (cursorX == widgetSize.width - widgetCaption.length && inputText.length > cursorX){ 276 scrollX = inputText.length - (widgetSize.width - widgetCaption.length); 277 } 278 } 279 } 280 } 281 //set cursor pos, if can 282 if (cursorPos !is null){ 283 cursorPos(widgetPosition.x + widgetCaption.length + (cursorX - scrollX), widgetPosition.y); 284 } 285 } 286 ///The text that has been input-ed. 287 @property string text(){ 288 return cast(string)inputText; 289 } 290 ///The text that has been input-ed. 291 @property string text(string newText){ 292 inputText = cast(char[])newText; 293 if (forceUpdate !is null){ 294 forceUpdate(); 295 } 296 return cast(string)inputText; 297 } 298 } 299 300 /// Can be used as a simple text editor, or to just display text 301 /// 302 /// Name in theme: 'memo'; 303 class MemoWidget : QWidget{ 304 private: 305 List!string widgetLines; 306 uinteger scrollX, scrollY; 307 uinteger cursorX, cursorY; 308 RGBColor bgColor, textColor; 309 bool writeProtected = false; 310 // used by widget itseld to recalculate scrolling 311 void reScroll(){ 312 //calculate scrollY first 313 if ((scrollY + widgetSize.height < cursorY || scrollY + widgetSize.height >= cursorY) && cursorY != 0){ 314 if (cursorY <= widgetSize.height/2){ 315 scrollY = 0; 316 }else{ 317 scrollY = cursorY - (widgetSize.height/2); 318 } 319 } 320 //now time for scrollX 321 //check if is within length of line 322 uinteger len = widgetLines.read(cursorY).length; 323 if (cursorX > len){ 324 cursorX = len; 325 } 326 //now calculate scrollX, if it needs to be increased 327 if (/*cursorX > widgetSize.width &&*/(scrollX + widgetSize.width < cursorX || scrollX + widgetSize.width >= cursorX)){ 328 if (cursorX <= widgetSize.width/2){ 329 scrollX = 0; 330 }else{ 331 scrollX = cursorX - (widgetSize.width/2); 332 } 333 } 334 } 335 // used by widget itself to set cursor 336 void setCursor(){ 337 //put the cursor at correct position, if possible 338 if (cursorPos !is null){ 339 //check if cursor is at a position that's possible 340 if (widgetLines.length >= cursorY && widgetLines.read(cursorY).length >= cursorX){ 341 cursorPos((cursorX - scrollX)+widgetPosition.x, (cursorY - scrollY)+widgetPosition.y); 342 } 343 } 344 } 345 // used by widget itself to move cursor 346 void moveCursor(uinteger x, uinteger y){ 347 cursorX = x; 348 cursorY = y; 349 350 if (cursorY >= widgetLines.length){ 351 cursorY = widgetLines.length-1; 352 } 353 if (cursorX >= widgetLines.read(cursorY).length){ 354 cursorX = widgetLines.read(cursorY).length; 355 } 356 } 357 public: 358 this(bool readOnly = false){ 359 widgetName = "memo"; 360 widgetLines = new List!string; 361 scrollX, scrollY = 0; 362 cursorX, cursorY = 0; 363 writeProtected = readOnly;//cause if readOnly, then writeProtected = true also 364 } 365 ~this(){ 366 delete widgetLines; 367 } 368 369 override void updateColors(){ 370 needsUpdate = true; 371 if (&widgetTheme && widgetTheme.hasColors(name, ["background", "text"])){ 372 bgColor = widgetTheme.getColor(name, "background"); 373 textColor = widgetTheme.getColor(name, "text"); 374 }else{ 375 bgColor = hexToColor("404040"); 376 textColor = hexToColor("00FF00"); 377 } 378 if (forceUpdate !is null){ 379 forceUpdate(); 380 } 381 } 382 383 override bool update(ref Matrix display){ 384 bool r = false; 385 if (needsUpdate){ 386 needsUpdate = false; 387 r = true; 388 //check if there's lines to be displayed 389 uinteger count = widgetLines.length, i, linesWritten = 0; 390 char[] emptyLine; 391 emptyLine.length = widgetSize.width; 392 emptyLine[0 .. emptyLine.length] = ' '; 393 if (count > 0){ 394 //write lines to memo 395 char[] line; 396 for (i = scrollY; i < count; i++){ 397 //echo current line 398 line = cast(char[])widgetLines.read(i); 399 //fit the line into screen, i.e check if only a part of it will be displayed 400 if (line.length >= widgetSize.width+scrollX){ 401 //display only partial line 402 display.write(line[scrollX .. scrollX + widgetSize.width], textColor, bgColor); 403 }else{ 404 //either the line is small enough to fit, or 0-length 405 if (line.length <= scrollX || line.length == 0){ 406 //just write the bgColor 407 display.write(emptyLine, textColor, bgColor); 408 }else{ 409 display.write(line[scrollX .. line.length], textColor, bgColor); 410 //write the bgColor 411 display.write(emptyLine[line.length - scrollX .. emptyLine.length], textColor, bgColor); 412 } 413 } 414 linesWritten ++; 415 //check if is at end 416 if (i-scrollY >= widgetSize.height){ 417 break; 418 } 419 } 420 //fill empty space with emptyLine 421 if (linesWritten < widgetSize.height){ 422 count = widgetSize.height; 423 for (i = linesWritten; i < count; i++){ 424 display.write(emptyLine,textColor, bgColor); 425 } 426 } 427 //put the cursor at correct position, if possible 428 setCursor(); 429 } 430 } 431 return r; 432 } 433 434 override void mouseEvent(MouseClick mouse){ 435 super.mouseEvent(mouse); 436 //calculate mouse position, relative to scroll and widgetPosition 437 mouse.x = (mouse.x - widgetPosition.x) + scrollX; 438 mouse.y = (mouse.y - widgetPosition.y) + scrollY; 439 if (mouse.mouseButton == mouse.Button.Left){ 440 needsUpdate = true; 441 moveCursor(mouse.x, mouse.y); 442 443 }else if (mouse.mouseButton == mouse.Button.ScrollDown){ 444 if (cursorY < widgetLines.length){ 445 needsUpdate = true; 446 moveCursor(cursorX, cursorY + 4); 447 reScroll(); 448 } 449 }else if (mouse.mouseButton == mouse.Button.ScrollUp){ 450 if (cursorY > 0){ 451 needsUpdate = true; 452 if (cursorY < 4){ 453 moveCursor(cursorX, 0); 454 }else{ 455 moveCursor(cursorX, cursorY - 4); 456 } 457 reScroll(); 458 } 459 } 460 } 461 462 override void keyboardEvent(KeyPress key){ 463 super.keyboardEvent(key); 464 if (key.isChar){ 465 if (!writeProtected){ 466 needsUpdate = true; 467 string currentLine = widgetLines.read(cursorY); 468 //check if backspace 469 if (key.key == '\b'){ 470 //make sure that it's not the first line, first line cannot be removed 471 if (cursorY > 0){ 472 //check if has to remove a '\n' 473 if (cursorX == 0){ 474 cursorY --; 475 //if line's not empty, append it to previous line 476 cursorX = widgetLines.read(cursorY).length; 477 if (currentLine != ""){ 478 //else, append this line to previous 479 widgetLines.set(cursorY, widgetLines.read(cursorY)~currentLine); 480 } 481 widgetLines.remove(cursorY+1); 482 }else{ 483 widgetLines.set(cursorY, cast(string)deleteArray(cast(char[])currentLine,cursorX-1)); 484 cursorX --; 485 } 486 }else if (cursorX > 0){ 487 widgetLines.set(cursorY, cast(string)deleteArray(cast(char[])currentLine,cursorX-1)); 488 cursorX --; 489 } 490 491 }else if (key.key == '\n'){ 492 //insert a newline 493 //if is at end, just add it 494 bool atEnd = false; 495 if (cursorY >= widgetLines.length - 1){ 496 atEnd = true; 497 } 498 if (cursorX == widgetLines.read(cursorY).length){ 499 if (atEnd){ 500 widgetLines.add(""); 501 }else{ 502 widgetLines.insert(cursorY + 1,""); 503 } 504 }else{ 505 string[2] line; 506 line[0] = widgetLines.read(cursorY); 507 line[1] = line[0][cursorX .. line[0].length]; 508 line[0] = line[0][0 .. cursorX]; 509 widgetLines.set(cursorY, line[0]); 510 if (atEnd){ 511 widgetLines.add(line[1]); 512 }else{ 513 widgetLines.insert(cursorY + 1, line[1]); 514 } 515 } 516 cursorY ++; 517 cursorX = 0; 518 }else if (key.key == '\t'){ 519 //convert it to 4 spaces 520 widgetLines.set(cursorY, cast(string)insertArray(cast(char[])currentLine,cast(char[])" ",cursorX)); 521 cursorX += 4; 522 }else{ 523 //insert that char 524 widgetLines.set(cursorY, cast(string)insertArray(cast(char[])currentLine,[cast(char)key.key],cursorX)); 525 cursorX ++; 526 } 527 } 528 }else{ 529 if (key.key == key.NonCharKey.Delete){ 530 needsUpdate = true; 531 //check if is deleting \n 532 if (cursorX == widgetLines.read(cursorY).length && cursorY < widgetLines.length-1){ 533 //merge next line with this one 534 char[] line = cast(char[])widgetLines.read(cursorY)~widgetLines.read(cursorY+1); 535 widgetLines.set(cursorY, cast(string)line); 536 //remove next line 537 widgetLines.remove(cursorY+1); 538 }else if (cursorX < widgetLines.read(cursorY).length){ 539 char[] line = cast(char[])widgetLines.read(cursorY); 540 line = line.deleteArray(cursorX); 541 widgetLines.set(cursorY, cast(string)line); 542 } 543 }else if (key.key == key.NonCharKey.DownArrow){ 544 if (cursorY < widgetLines.length-1){ 545 needsUpdate = true; 546 cursorY ++; 547 } 548 }else if (key.key == key.NonCharKey.UpArrow){ 549 if (cursorY > 0){ 550 needsUpdate = true; 551 cursorY --; 552 } 553 }else if (key.key == key.NonCharKey.LeftArrow){ 554 if ((cursorY >= 0 && cursorX > 0) || (cursorY > 0 && cursorX == 0)){ 555 needsUpdate = true; 556 uinteger x, y; 557 if (cursorX == 0){ 558 cursorY --; 559 cursorX = widgetLines.read(cursorY).length; 560 }else{ 561 cursorX --; 562 } 563 } 564 }else if (key.key == key.NonCharKey.RightArrow){ 565 needsUpdate = true; 566 if (cursorX == widgetLines.read(cursorY).length){ 567 if (cursorY < widgetLines.length-1){ 568 cursorX = 0; 569 cursorY ++; 570 scrollX = 0; 571 572 } 573 }else{ 574 cursorX ++; 575 } 576 } 577 } 578 reScroll(); 579 } 580 581 ///returns a list of lines in memo 582 /// 583 ///To modify the content, just modify it in the returned list 584 /// 585 ///class `List` is defined in `qui.lists.d` 586 @property List!string lines(){ 587 return widgetLines; 588 } 589 ///Returns true if memo's contents cannot be modified, by user 590 @property bool readOnly(){ 591 return writeProtected; 592 } 593 ///sets whether to allow modifying of contents (false) or not (true) 594 @property bool readOnly(bool newPermission){ 595 writeProtected = newPermission; 596 if (forceUpdate !is null){ 597 forceUpdate(); 598 } 599 return writeProtected; 600 } 601 } 602 603 /// Displays an un-scrollable log, that removes older lines 604 /// 605 /// It's content cannot be modified by user. 606 /// 607 /// Name in theme: 'log'; 608 class LogWidget : QWidget{ 609 private: 610 LogList!string logs; 611 612 RGBColor bgColor, textColor; 613 614 uinteger stringLineCount(string s){ 615 uinteger width = widgetSize.width; 616 uinteger i, widthTaken = 0, count = 1; 617 for (i = 0; i < s.length; i++){ 618 widthTaken ++; 619 if (s[i] == '\n' || widthTaken >= width){ 620 count ++; 621 widthTaken = 0; 622 } 623 if (widthTaken >= width){ 624 count ++; 625 } 626 } 627 return count; 628 } 629 string stripString(string s, uinteger height){ 630 char[] r; 631 uinteger width = widgetSize.width; 632 uinteger i, widthTaken = 0, count = 1; 633 for (i = 0; i < s.length; i++){ 634 widthTaken ++; 635 if (s[i] == '\n' || widthTaken >= width){ 636 count ++; 637 widthTaken = 0; 638 } 639 if (count > height){ 640 r.length = i; 641 r[0 .. i] = s[0 .. i]; 642 break; 643 } 644 } 645 return cast(string)r; 646 } 647 public: 648 this(uinteger maxLen=100){ 649 widgetName = "log"; 650 logs = new LogList!string(maxLen); 651 } 652 ~this(){ 653 delete logs; 654 } 655 656 override public void updateColors(){ 657 needsUpdate = true; 658 if (&widgetTheme && widgetTheme.hasColors(name, ["background", "text"])){ 659 bgColor = widgetTheme.getColor(name, "background"); 660 textColor = widgetTheme.getColor(name, "text"); 661 }else{ 662 bgColor = hexToColor("404040"); 663 textColor = hexToColor("00FF00"); 664 } 665 if (forceUpdate !is null){ 666 forceUpdate(); 667 } 668 } 669 670 override public bool update(ref Matrix display){ 671 bool r = false; 672 if (needsUpdate){ 673 needsUpdate = false; 674 r = true; 675 //get list of messages 676 string[] messages = logs.read(logs.maxCapacity); 677 //set colors 678 display.setColors(textColor, bgColor); 679 //determine how many of them will be displayed 680 uinteger count;//right now, it's used to store number of lines used 681 uinteger i; 682 if (messages.length>0){ 683 for (i=messages.length-1; i>=0; i--){ 684 count += stringLineCount(messages[i]); 685 if (count > widgetSize.height){ 686 messages = messages[i+1 .. messages.length]; 687 //try to insert part of the last message 688 uinteger thisCount = stringLineCount(messages[i]); 689 count -= thisCount; 690 if (count < widgetSize.height){ 691 messages ~= [stripString(messages[i], widgetSize.height - count)]; 692 } 693 break; 694 } 695 if (i==0){ 696 break; 697 } 698 } 699 } 700 //write them 701 for (i = 0; i < messages.length; i++){ 702 display.write(cast(char[])messages[i], textColor, bgColor); 703 //add newline 704 display.moveTo(0, display.writePosY+1); 705 } 706 } 707 return r; 708 } 709 710 ///adds string to the log, and scrolls down to it 711 void add(string item){ 712 //add height 713 logs.add(item); 714 //update 715 needsUpdate = true; 716 if (forceUpdate !is null){ 717 forceUpdate(); 718 } 719 } 720 ///clears the log 721 void clear(){ 722 logs.reset(); 723 //update 724 needsUpdate = true; 725 if (forceUpdate !is null){ 726 forceUpdate(); 727 } 728 } 729 } 730 731 /// A button 732 /// 733 /// the caption is displayed inside the button 734 /// 735 /// To receive input, set the `onMouseEvent` to set a custom mouse event 736 class ButtonWidget : QWidget{ 737 private: 738 RGBColor bgColor, textColor; 739 public: 740 this(){ 741 widgetName = "button"; 742 } 743 override public void updateColors(){ 744 if (&widgetTheme && widgetTheme.hasColors(name, ["background", "text"])){ 745 bgColor = widgetTheme.getColor(name, "background"); 746 textColor = widgetTheme.getColor(name, "text"); 747 }else{ 748 bgColor = hexToColor("00FF00"); 749 textColor = hexToColor("000000"); 750 } 751 } 752 753 override public bool update(ref Matrix display) { 754 bool r = false; 755 if (needsUpdate){ 756 r = true; 757 needsUpdate = false; 758 char[] row; 759 row.length = widgetSize.width; 760 row[0 .. row.length] = ' '; 761 //write the caption too! 762 uinteger middle = widgetSize.height/2; 763 for (uinteger i = 0; i < widgetSize.height; i++){ 764 if (i == middle && widgetCaption != ""){ 765 row = centerAlignText(cast(char[])caption, widgetSize.width); 766 display.write(row, textColor, bgColor); 767 row[0 .. row.length] = ' '; 768 continue; 769 }else{ 770 display.write(row, textColor, bgColor); 771 } 772 } 773 } 774 return r; 775 } 776 }