1 /**************************************************************************** 2 ** 3 ** DQt - D bindings for the Qt Toolkit 4 ** 5 ** GNU Lesser General Public License Usage 6 ** This file may be used under the terms of the GNU Lesser 7 ** General Public License version 3 as published by the Free Software 8 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 9 ** packaging of this file. Please review the following information to 10 ** ensure the GNU Lesser General Public License version 3 requirements 11 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 12 ** 13 ****************************************************************************/ 14 module qt.widgets.ui; 15 16 import qt.helpers; 17 import std.array; 18 19 private void writeStringLiteral(R)(ref Appender!string code, R str) 20 { 21 code.put("\""); 22 foreach(dchar c; str) 23 { 24 if(c == '\"') 25 code.put("\\\""); 26 else if(c == '\n') 27 code.put("\\n\" ~\n\""); 28 else if(c == '\r') 29 code.put("\\r"); 30 else if(c == '\\') 31 code.put("\\\\"); 32 else 33 code.put(c); 34 } 35 code.put("\""); 36 } 37 38 private struct UICodeWriter() 39 { 40 import qt.widgets.internal.dxml.dom; 41 import qt.widgets.internal.dxml.util; 42 import std.ascii; 43 import std.uni; 44 import std.conv; 45 import std.algorithm; 46 import std.exception; 47 alias DOMEntity = qt.widgets.internal.dxml.dom.DOMEntity!string; 48 49 string customWidgetPackage; 50 Appender!string codeVars; 51 Appender!string codeSetup; 52 Appender!string codeSetupAddActions; 53 Appender!string codeSetupConnect; 54 Appender!string codeSetupDelayed; 55 Appender!string codeRetranslate; 56 size_t[string] tmpCountSetup; 57 size_t[string] tmpCountRetranslate; 58 string[string] widgetTypes; 59 60 string createTmpVar(ref size_t[string] tmpCount, string prefix) 61 { 62 string r; 63 if(prefix !in tmpCount) 64 { 65 r = prefix; 66 tmpCount[prefix] = 1; 67 } 68 else 69 { 70 r = text(prefix, tmpCount[prefix]); 71 tmpCount[prefix]++; 72 } 73 return r; 74 } 75 76 struct WidgetInfo 77 { 78 string name; 79 string className; 80 } 81 82 WidgetInfo rootWidgetInfo; 83 84 WidgetInfo getWidgetInfo(DOMEntity widget) 85 { 86 WidgetInfo info; 87 foreach(attr; widget.attributes) 88 { 89 if(attr.name == "class") 90 info.className = attr.value; 91 else if(attr.name == "name") 92 info.name = attr.value; 93 } 94 if(widget.name == "action") 95 info.className = "QAction"; 96 if(widget.name == "spacer") 97 info.className = "QSpacerItem"; 98 return info; 99 } 100 101 string getWidgetModule(string name) 102 { 103 if(name == "QSpacerItem") 104 return "qt.widgets.layoutitem"; 105 if(name == "QVBoxLayout") 106 return "qt.widgets.boxlayout"; 107 if(name == "QHBoxLayout") 108 return "qt.widgets.boxlayout"; 109 if(name == "QTimeEdit") 110 return "qt.widgets.datetimeedit"; 111 if(name == "QDateEdit") 112 return "qt.widgets.datetimeedit"; 113 if(name == "QDoubleSpinBox") 114 return "qt.widgets.spinbox"; 115 116 if(name.startsWith("Q")) 117 return "qt.widgets." ~ std.uni.toLower(name[1..$]); 118 119 if(customWidgetPackage.length) 120 return customWidgetPackage ~ "." ~ name.toLower; 121 else 122 return name.toLower; 123 } 124 125 void addProperty(DOMEntity property, string widgetName, string widgetType, bool isTopLevel, string extraArg) 126 { 127 string name; 128 foreach(attr; property.attributes) 129 { 130 if(attr.name == "name") 131 name = attr.value; 132 } 133 134 enforce(property.children.length == 1); 135 136 bool noTr; 137 string comment; 138 foreach(attr; property.children[0].attributes) 139 { 140 if(attr.name == "notr" && attr.value == "true") 141 noTr = true; 142 if(attr.name == "comment") 143 comment = attr.value; 144 } 145 146 Appender!string* code = &codeSetup; 147 size_t[string]* tmpCount = &tmpCountSetup; 148 if(property.children[0].name == "string" && !noTr) 149 { 150 code = &codeRetranslate; 151 tmpCount = &tmpCountRetranslate; 152 } 153 else if(name == "currentIndex") 154 code = &codeSetupDelayed; 155 156 if(widgetType == "Line" && name == "orientation") 157 { 158 enforce(property.children[0].name == "enum"); 159 enforce(property.children[0].children.length == 1); 160 enforce(property.children[0].children[0].type == EntityType.text); 161 string value = property.children[0].children[0].text; 162 163 code.put(" "); 164 code.put(widgetName); 165 code.put(".setFrameShape(imported!q{qt.widgets.frame}.QFrame.Shape."); 166 if(value == "Qt::Horizontal") 167 code.put("HLine"); 168 else if(value == "Qt::Vertical") 169 code.put("VLine"); 170 else 171 enforce(false); 172 code.put(");\n"); 173 174 code.put(" "); 175 code.put(widgetName); 176 code.put("."); 177 code.put("setFrameShadow(imported!q{qt.widgets.frame}.QFrame.Shadow.Sunken);\n"); 178 return; 179 } 180 181 Appender!string codeValue; 182 bool needTmp; 183 string methodName = "set" ~ std.ascii.toUpper(name[0]) ~ name[1..$]; 184 if(property.children[0].name == "rect") 185 { 186 if(isTopLevel) 187 { 188 methodName = "resize"; 189 foreach(i, c; property.children[0].children) 190 { 191 if(i < 2) 192 continue; 193 if(i > 2) 194 codeValue.put(", "); 195 enforce(c.children.length == 1); 196 enforce(c.children[0].type == EntityType.text); 197 codeValue.put(c.children[0].text); 198 } 199 } 200 else 201 { 202 codeValue.put("imported!q{qt.core.rect}.QRect("); 203 foreach(i, c; property.children[0].children) 204 { 205 if(i) 206 codeValue.put(", "); 207 enforce(c.children.length == 1); 208 enforce(c.children[0].type == EntityType.text); 209 codeValue.put(c.children[0].text); 210 } 211 codeValue.put(")"); 212 needTmp = true; 213 } 214 } 215 else if(property.children[0].name == "color") 216 { 217 codeValue.put("imported!q{qt.gui.color}.QColor("); 218 foreach(i, c; property.children[0].children) 219 { 220 if(i) 221 codeValue.put(", "); 222 enforce(c.children.length == 1); 223 enforce(c.children[0].type == EntityType.text); 224 codeValue.put(c.children[0].text); 225 } 226 codeValue.put(")"); 227 } 228 else if(property.children[0].name.among("bool", "number", "double")) 229 { 230 enforce(property.children[0].children.length == 1); 231 enforce(property.children[0].children[0].type == EntityType.text); 232 codeValue.put(property.children[0].children[0].text); 233 } 234 else if(property.children[0].name == "set") 235 { 236 enforce(property.children[0].children.length == 1); 237 enforce(property.children[0].children[0].type == EntityType.text); 238 codeValue.put("imported!q{qt.core.flags}.flagsFromStaticString!(typeof("); 239 codeValue.put(widgetName); 240 codeValue.put("."); 241 codeValue.put(name); 242 codeValue.put("()), q{"); 243 codeValue.put(property.children[0].children[0].text); 244 codeValue.put("})"); 245 } 246 else if(property.children[0].name == "enum") 247 { 248 enforce(property.children[0].children.length == 1); 249 enforce(property.children[0].children[0].type == EntityType.text); 250 codeValue.put("imported!q{qt.core.flags}.enumFromStaticString!(typeof("); 251 codeValue.put(widgetName); 252 codeValue.put("."); 253 codeValue.put(name); 254 codeValue.put("()), q{"); 255 codeValue.put(property.children[0].children[0].text); 256 codeValue.put("})"); 257 } 258 else if(property.children[0].name == "string") 259 { 260 string value; 261 if(property.children[0].children.length) 262 { 263 enforce(property.children[0].children.length == 1); 264 enforce(property.children[0].children[0].type == EntityType.text); 265 value = property.children[0].children[0].text; 266 } 267 if(name == "title" && property.name == "attribute") 268 methodName = "setTabText"; 269 if(name == "text" && widgetType == "QComboBox") 270 methodName = "setItemText"; 271 if(value.length == 0) 272 { 273 if(widgetType == "QTreeWidget") 274 return; 275 codeValue.put("imported!q{qt.core.string}.QString.create()"); 276 } 277 else if(noTr) 278 { 279 codeValue.put("imported!q{qt.core.string}.QString.fromUtf8("); 280 writeStringLiteral(codeValue, value.asDecodedXML); 281 codeValue.put(")"); 282 } 283 else 284 { 285 codeValue.put("imported!q{qt.core.coreapplication}.QCoreApplication.translate(\""); 286 codeValue.put(rootWidgetInfo.name); 287 codeValue.put("\", "); 288 writeStringLiteral(codeValue, value.asDecodedXML); 289 codeValue.put(", "); 290 if(comment.length) 291 writeStringLiteral(codeValue, comment.asDecodedXML); 292 else 293 codeValue.put("null"); 294 codeValue.put(")"); 295 } 296 } 297 else if(property.children[0].name == "sizepolicy") 298 { 299 string hsizetype="Expanding", vsizetype="Expanding"; 300 foreach(attr; property.children[0].attributes) 301 { 302 if(attr.name == "hsizetype") 303 hsizetype = attr.value; 304 if(attr.name == "vsizetype") 305 vsizetype = attr.value; 306 } 307 308 string sizePolicyVar = createTmpVar(*tmpCount, "sizePolicy"); 309 code.put(" auto "); 310 code.put(sizePolicyVar); 311 code.put(" = imported!q{qt.widgets.sizepolicy}.QSizePolicy(imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy."); 312 code.put(hsizetype); 313 code.put(", imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy."); 314 code.put(vsizetype); 315 code.put(");\n"); 316 317 foreach(i, c; property.children[0].children) 318 { 319 enforce(c.children.length == 1); 320 enforce(c.children[0].type == EntityType.text); 321 code.put(" "); 322 code.put(sizePolicyVar); 323 code.put("."); 324 if(c.name == "horstretch") 325 code.put("setHorizontalStretch"); 326 else if(c.name == "verstretch") 327 code.put("setVerticalStretch"); 328 else 329 enforce(false); 330 code.put("("); 331 code.put(c.children[0].text); 332 code.put(");\n"); 333 } 334 335 code.put(" "); 336 code.put(sizePolicyVar); 337 code.put("."); 338 code.put("setHeightForWidth"); 339 code.put("("); 340 code.put(widgetName); 341 code.put(".sizePolicy().hasHeightForWidth());\n"); 342 343 codeValue.put(sizePolicyVar); 344 } 345 else 346 enforce(false, "Unsupported property type " ~ property.children[0].name); 347 348 code.put(" "); 349 string tmpVar; 350 if(needTmp) 351 { 352 tmpVar = createTmpVar(*tmpCount, "tmp"); 353 code.put("auto "); 354 code.put(tmpVar); 355 code.put(" = "); 356 code.put(codeValue.data); 357 code.put("; "); 358 } 359 code.put(widgetName); 360 code.put("."); 361 code.put(methodName); 362 code.put("("); 363 code.put(extraArg); 364 if(needTmp) 365 code.put(tmpVar); 366 else 367 code.put(codeValue.data); 368 code.put(");\n"); 369 } 370 371 WidgetInfo addItem(DOMEntity widget, string parentName, string parentType, bool isRoot, size_t itemIndex) 372 { 373 WidgetInfo info = getWidgetInfo(widget); 374 375 string extraArg; 376 string name; 377 if(parentType == "QComboBox") 378 { 379 codeSetup.put(" "); 380 codeSetup.put(parentName); 381 codeSetup.put(".addItem(imported!q{qt.core.string}.QString.create());\n"); 382 extraArg = text(itemIndex, ", "); 383 name = parentName; 384 } 385 else if(parentType == "QTreeWidget") 386 { 387 name = createTmpVar(tmpCountSetup, "__qtreewidgetitem"); 388 codeSetup.put(" imported!q{qt.widgets.treewidget}.QTreeWidgetItem "); 389 codeSetup.put(name); 390 codeSetup.put(" = cpp_new!(imported!q{qt.widgets.treewidget}.QTreeWidgetItem)("); 391 codeSetup.put(parentName); 392 codeSetup.put(");\n"); 393 394 codeRetranslate.put(" imported!q{qt.widgets.treewidget}.QTreeWidgetItem "); 395 codeRetranslate.put(name); 396 codeRetranslate.put(" = "); 397 codeRetranslate.put(parentName); 398 codeRetranslate.put(isRoot ? ".topLevelItem(" : ".child("); 399 codeRetranslate.put(text(itemIndex)); 400 codeRetranslate.put(");\n"); 401 } 402 else if(parentType == "column") 403 { 404 extraArg = text(itemIndex, ", "); 405 name = parentName; 406 } 407 size_t[string] columnByProperty; 408 foreach(property; widget.children) 409 { 410 if(parentType == "QTreeWidget") 411 { 412 string propName; 413 foreach(attr; property.attributes) 414 { 415 if(attr.name == "name") 416 propName = attr.value; 417 } 418 if(propName == "flags") 419 extraArg = ""; 420 else 421 { 422 size_t col = columnByProperty.get(propName, 0); 423 extraArg = text(col, ", "); 424 columnByProperty[propName] = col + 1; 425 } 426 } 427 if(property.name == "property") 428 addProperty(property, name, parentType, false, extraArg); 429 } 430 size_t childItemCount; 431 foreach(c; widget.children) 432 { 433 if(c.name == "item") 434 { 435 addItem(c, name, parentType, false, childItemCount); 436 childItemCount++; 437 } 438 } 439 return info; 440 } 441 442 WidgetInfo addWidget(DOMEntity widget, string parentName, bool parentIsLayout) 443 { 444 WidgetInfo info = getWidgetInfo(widget); 445 446 if(info.name.length) 447 { 448 widgetTypes[info.name] = info.className; 449 } 450 451 if(widget.name == "spacer") 452 { 453 string orientation; 454 string sizeType = "Expanding"; 455 string width = "0"; 456 string height = "0"; 457 458 foreach(property; widget.children) 459 { 460 if(property.name == "property") 461 { 462 string propName; 463 foreach(attr; property.attributes) 464 { 465 if(attr.name == "name") 466 propName = attr.value; 467 } 468 469 if(propName == "orientation") 470 { 471 enforce(property.children.length == 1); 472 enforce(property.children[0].name == "enum"); 473 enforce(property.children[0].children.length == 1); 474 orientation = property.children[0].children[0].text; 475 } 476 else if(propName == "sizeType") 477 { 478 enforce(property.children.length == 1); 479 enforce(property.children[0].name == "enum"); 480 enforce(property.children[0].children.length == 1); 481 sizeType = property.children[0].children[0].text; 482 enforce(sizeType.startsWith("QSizePolicy::")); 483 sizeType = sizeType["QSizePolicy::".length..$]; 484 } 485 else if(propName == "sizeHint") 486 { 487 enforce(property.children.length == 1); 488 enforce(property.children[0].name == "size"); 489 enforce(property.children[0].children.length == 2); 490 enforce(property.children[0].children[0].name == "width"); 491 enforce(property.children[0].children[1].name == "height"); 492 width = property.children[0].children[0].children[0].text; 493 height = property.children[0].children[1].children[0].text; 494 } 495 } 496 } 497 498 codeVars.put(" "); 499 codeVars.put("imported!q{"); 500 codeVars.put(getWidgetModule(info.className)); 501 codeVars.put("}."); 502 codeVars.put(info.className); 503 codeVars.put(" "); 504 codeVars.put(info.name); 505 codeVars.put(";\n"); 506 507 codeSetup.put(" "); 508 codeSetup.put(info.name); 509 codeSetup.put(" = cpp_new!(imported!q{"); 510 codeSetup.put(getWidgetModule(info.className)); 511 codeSetup.put("}."); 512 codeSetup.put(info.className); 513 codeSetup.put(")("); 514 codeSetup.put(width); 515 codeSetup.put(", "); 516 codeSetup.put(height); 517 codeSetup.put(", "); 518 if(orientation == "Qt::Horizontal") 519 { 520 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy."); 521 codeSetup.put(sizeType); 522 codeSetup.put(", "); 523 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.Minimum"); 524 } 525 else if(orientation == "Qt::Vertical") 526 { 527 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy.Minimum"); 528 codeSetup.put(", "); 529 codeSetup.put("imported!q{qt.widgets.sizepolicy}.QSizePolicy.Policy."); 530 codeSetup.put(sizeType); 531 } 532 else 533 enforce(false); 534 codeSetup.put(");\n"); 535 return info; 536 } 537 538 if(info.name.length) 539 { 540 codeVars.put(" "); 541 if(info.className == "Line") 542 { 543 codeVars.put("imported!q{qt.widgets.frame}.QFrame"); 544 } 545 else 546 { 547 codeVars.put("imported!q{"); 548 codeVars.put(getWidgetModule(info.className)); 549 codeVars.put("}."); 550 codeVars.put(info.className); 551 } 552 codeVars.put(" "); 553 codeVars.put(info.name); 554 codeVars.put(";\n"); 555 556 codeSetup.put(" "); 557 codeSetup.put(info.name); 558 codeSetup.put(" = cpp_new!("); 559 if(info.className == "Line") 560 { 561 codeSetup.put("imported!q{qt.widgets.frame}.QFrame"); 562 } 563 else 564 { 565 codeSetup.put("imported!q{"); 566 codeSetup.put(getWidgetModule(info.className)); 567 codeSetup.put("}."); 568 codeSetup.put(info.className); 569 } 570 codeSetup.put(")("); 571 if(!(parentIsLayout && widget.name == "layout")) 572 codeSetup.put(parentName); 573 codeSetup.put(");\n"); 574 } 575 string sortingEnabledVar; 576 if(info.className == "QTreeWidget") 577 { 578 string headerName = createTmpVar(tmpCountSetup, "__qtreewidgetitem"); 579 codeSetup.put(" imported!q{qt.widgets.treewidget}.QTreeWidgetItem "); 580 codeSetup.put(headerName); 581 codeSetup.put(" = cpp_new!(imported!q{qt.widgets.treewidget}.QTreeWidgetItem)("); 582 codeSetup.put(");\n"); 583 584 codeRetranslate.put(" imported!q{qt.widgets.treewidget}.QTreeWidgetItem "); 585 codeRetranslate.put(headerName); 586 codeRetranslate.put(" = "); 587 codeRetranslate.put(info.name); 588 codeRetranslate.put(".headerItem();\n"); 589 590 size_t childItemCount; 591 foreach(c; widget.children) 592 { 593 if(c.name == "column") 594 { 595 WidgetInfo childInfo = addItem(c, headerName, "column", true, childItemCount); 596 childItemCount++; 597 } 598 } 599 600 codeSetup.put(" "); 601 codeSetup.put(info.name); 602 codeSetup.put(".setHeaderItem("); 603 codeSetup.put(headerName); 604 codeSetup.put(");\n"); 605 606 sortingEnabledVar = createTmpVar(tmpCountRetranslate, "__sortingEnabled"); 607 codeRetranslate.put(" const(bool) "); 608 codeRetranslate.put(sortingEnabledVar); 609 codeRetranslate.put(" = "); 610 codeRetranslate.put(info.name); 611 codeRetranslate.put(".isSortingEnabled();\n"); 612 613 codeRetranslate.put(" "); 614 codeRetranslate.put(info.name); 615 codeRetranslate.put(".setSortingEnabled(false);\n"); 616 } 617 size_t childItemCount; 618 foreach(c; widget.children) 619 { 620 if(c.name == "item" && widget.name != "layout") 621 { 622 WidgetInfo childInfo = addItem(c, info.name, info.className, true, childItemCount); 623 childItemCount++; 624 } 625 } 626 if(info.name.length) 627 { 628 codeSetup.put(" "); 629 codeSetup.put(info.name); 630 codeSetup.put(".setObjectName(\""); 631 codeSetup.put(info.name); 632 codeSetup.put("\");\n"); 633 } 634 635 string constructorArg = info.name; 636 if(info.className == "QTabWidget") 637 constructorArg = ""; 638 if(info.className == "QScrollArea") 639 constructorArg = ""; 640 if(widget.name == "layout") 641 constructorArg = parentName; 642 if(widget.name == "item") 643 constructorArg = parentName; 644 foreach(property; widget.children) 645 { 646 if(property.name == "property") 647 addProperty(property, info.name, info.className, false, ""); 648 } 649 foreach(c; widget.children) 650 { 651 WidgetInfo childInfo = addChildWidget(widget, info, c, constructorArg, widget.name == "layout"); 652 if(info.className == "QTabWidget") 653 { 654 foreach(property; c.children) 655 { 656 if(property.name == "attribute") 657 addProperty(property, info.name, info.className, false, info.name ~ ".indexOf(" ~ childInfo.name ~ "), "); 658 } 659 } 660 } 661 if(info.className == "QTreeWidget") 662 { 663 codeRetranslate.put(" "); 664 codeRetranslate.put(info.name); 665 codeRetranslate.put(".setSortingEnabled("); 666 codeRetranslate.put(sortingEnabledVar); 667 codeRetranslate.put(");\n"); 668 } 669 return info; 670 } 671 672 WidgetInfo addChildWidget(DOMEntity widget, WidgetInfo info, DOMEntity c, string constructorArg, bool parentIsLayout) 673 { 674 if(c.name == "item" && widget.name == "layout") 675 { 676 enforce(c.children.length == 1); 677 WidgetInfo childInfo = addWidget(c.children[0], constructorArg, parentIsLayout); 678 679 string row, column, rowspan = "1", colspan = "1"; 680 foreach(attr; c.attributes) 681 { 682 if(attr.name == "row") 683 row = attr.value; 684 if(attr.name == "column") 685 column = attr.value; 686 if(attr.name == "rowspan") 687 rowspan = attr.value; 688 if(attr.name == "colspan") 689 colspan = attr.value; 690 } 691 692 codeSetup.put("\n "); 693 codeSetup.put(info.name); 694 if(info.className == "QFormLayout") 695 { 696 if(c.children[0].name == "layout") 697 codeSetup.put(".setLayout("); 698 else if(c.children[0].name == "spacer") 699 codeSetup.put(".setItem("); 700 else 701 codeSetup.put(".setWidget("); 702 703 codeSetup.put(row); 704 codeSetup.put(", imported!q{qt.widgets.formlayout}.QFormLayout.ItemRole."); 705 codeSetup.put((column == "1") ? "FieldRole" : "LabelRole"); 706 codeSetup.put(", "); 707 } 708 else 709 { 710 if(c.children[0].name == "layout") 711 codeSetup.put(".addLayout("); 712 else if(c.children[0].name == "spacer") 713 codeSetup.put(".addItem("); 714 else 715 codeSetup.put(".addWidget("); 716 } 717 codeSetup.put(childInfo.name); 718 if(info.className == "QGridLayout") 719 { 720 codeSetup.put(", "); 721 codeSetup.put(row); 722 codeSetup.put(", "); 723 codeSetup.put(column); 724 codeSetup.put(", "); 725 codeSetup.put(rowspan); 726 codeSetup.put(", "); 727 codeSetup.put(colspan); 728 } 729 codeSetup.put(");\n\n"); 730 return childInfo; 731 } 732 else if(c.name == "widget" || c.name == "layout") 733 { 734 WidgetInfo childInfo = addWidget(c, constructorArg, parentIsLayout); 735 736 if(info.className == "QTabWidget") 737 { 738 codeSetup.put(" "); 739 codeSetup.put(info.name); 740 codeSetup.put(".addTab("); 741 codeSetup.put(childInfo.name); 742 codeSetup.put(", "); 743 codeSetup.put("imported!q{qt.core.string}.QString.create()"); // TODO: not translateable strings 744 codeSetup.put(");\n"); 745 } 746 else if(info.className == "QMainWindow") 747 { 748 if(childInfo.className != "QMenuBar" && childInfo.className != "QStatusBar") 749 codeSetup.put("\n"); 750 codeSetup.put(" "); 751 codeSetup.put(info.name); 752 if(childInfo.className == "QMenuBar") 753 codeSetup.put(".setMenuBar("); 754 else if(childInfo.className == "QStatusBar") 755 codeSetup.put(".setStatusBar("); 756 else 757 codeSetup.put(".setCentralWidget("); 758 codeSetup.put(childInfo.name); 759 codeSetup.put(");\n"); 760 } 761 else if(info.className == "QSplitter") 762 { 763 codeSetup.put(" "); 764 codeSetup.put(info.name); 765 codeSetup.put(".addWidget("); 766 codeSetup.put(childInfo.name); 767 codeSetup.put(");\n"); 768 } 769 else if(info.className == "QScrollArea") 770 { 771 codeSetup.put(" "); 772 codeSetup.put(info.name); 773 codeSetup.put(".setWidget("); 774 codeSetup.put(childInfo.name); 775 codeSetup.put(");\n"); 776 } 777 return childInfo; 778 } 779 else if(c.name == "addaction") 780 { 781 string name; 782 foreach(attr; c.attributes) 783 { 784 if(attr.name == "name") 785 name = attr.value; 786 } 787 788 codeSetupAddActions.put(" "); 789 codeSetupAddActions.put(info.name); 790 codeSetupAddActions.put(".addAction("); 791 codeSetupAddActions.put(name); 792 if(name in widgetTypes && widgetTypes[name].among("QMenu", "QMenuBar")) 793 codeSetupAddActions.put(".menuAction()"); 794 codeSetupAddActions.put(");\n"); 795 } 796 return WidgetInfo.init; 797 } 798 799 void addConnection(DOMEntity connection) 800 { 801 string sender, signal, receiver, slot; 802 foreach(ref c; connection.children) 803 { 804 if(c.name == "sender") 805 { 806 enforce(c.children.length == 1); 807 enforce(c.children[0].type == EntityType.text); 808 sender = c.children[0].text; 809 } 810 else if(c.name == "signal") 811 { 812 enforce(c.children.length == 1); 813 enforce(c.children[0].type == EntityType.text); 814 signal = c.children[0].text; 815 } 816 else if(c.name == "receiver") 817 { 818 enforce(c.children.length == 1); 819 enforce(c.children[0].type == EntityType.text); 820 receiver = c.children[0].text; 821 } 822 else if(c.name == "slot") 823 { 824 enforce(c.children.length == 1); 825 enforce(c.children[0].type == EntityType.text); 826 slot = c.children[0].text; 827 } 828 } 829 830 // Ignore parameter types for signal and slot for now. 831 foreach(i, char c; signal) 832 if(c == '(') 833 { 834 signal = signal[0..i]; 835 break; 836 } 837 foreach(i, char c; slot) 838 if(c == '(') 839 { 840 slot = slot[0..i]; 841 break; 842 } 843 844 codeSetupConnect.put(" imported!q{qt.core.object}.QObject.connect("); 845 codeSetupConnect.put(sender); 846 codeSetupConnect.put(".signal!\""); 847 codeSetupConnect.put(signal); 848 codeSetupConnect.put("\", "); 849 codeSetupConnect.put(receiver); 850 codeSetupConnect.put(".slot!\""); 851 codeSetupConnect.put(slot); 852 codeSetupConnect.put("\");\n"); 853 } 854 855 string convert(string xml, string customWidgetPackage) 856 { 857 this.customWidgetPackage = customWidgetPackage; 858 auto dom = parseDOM!simpleXML(xml); 859 assert(dom.type == EntityType.elementStart); 860 861 enforce(dom.children.length == 1); 862 auto root = dom.children[0]; 863 enforce(root.name == "ui"); 864 865 DOMEntity* rootWidget; 866 foreach(ref c; root.children) 867 { 868 if(c.name == "widget") 869 { 870 enforce(rootWidget is null, "Multpile root widgets"); 871 rootWidget = &c; 872 } 873 } 874 enforce(rootWidget !is null); 875 rootWidgetInfo = getWidgetInfo(*rootWidget); 876 877 codeSetup.put(" if ("); 878 codeSetup.put(rootWidgetInfo.name); 879 codeSetup.put(".objectName().isEmpty())\n"); 880 codeSetup.put(" "); 881 codeSetup.put(rootWidgetInfo.name); 882 codeSetup.put(".setObjectName(\""); 883 codeSetup.put(rootWidgetInfo.name); 884 codeSetup.put("\");\n"); 885 886 foreach(c; rootWidget.children) 887 { 888 if(c.name == "property") 889 addProperty(c, rootWidgetInfo.name, rootWidgetInfo.className, true, ""); 890 } 891 892 foreach(c; rootWidget.children) 893 { 894 if(c.name == "action") 895 { 896 addWidget(c, rootWidgetInfo.name, false); 897 } 898 } 899 900 foreach(c; rootWidget.children) 901 { 902 addChildWidget(*rootWidget, rootWidgetInfo, c, rootWidgetInfo.name, false); 903 } 904 905 foreach(c; root.children) 906 { 907 if(c.name == "connections") 908 { 909 foreach(c2; c.children) 910 { 911 if(c2.name == "connection") 912 { 913 addConnection(c2); 914 } 915 } 916 } 917 } 918 919 codeVars.put("\n"); 920 codeVars.put(" void setupUi("); 921 codeVars.put("imported!q{"); 922 codeVars.put(getWidgetModule(rootWidgetInfo.className)); 923 codeVars.put("}."); 924 codeVars.put(rootWidgetInfo.className); 925 codeVars.put(" "); 926 codeVars.put(rootWidgetInfo.name); 927 codeVars.put(")\n"); 928 codeVars.put(" {\n"); 929 codeVars.put(" import core.stdcpp.new_: cpp_new;\n"); 930 codeVars.put(codeSetup.data); 931 codeVars.put(codeSetupAddActions.data); 932 codeVars.put("\n retranslateUi("); 933 codeVars.put(rootWidgetInfo.name); 934 codeVars.put(");\n"); 935 codeVars.put(codeSetupConnect.data); 936 codeVars.put(codeSetupDelayed.data); 937 codeVars.put("\n imported!q{qt.core.objectdefs}.QMetaObject.connectSlotsByName("); 938 codeVars.put(rootWidgetInfo.name); 939 codeVars.put(");\n"); 940 codeVars.put(" } // setupUi\n"); 941 codeVars.put("\n"); 942 codeVars.put(" void retranslateUi("); 943 codeVars.put("imported!q{"); 944 codeVars.put(getWidgetModule(rootWidgetInfo.className)); 945 codeVars.put("}."); 946 codeVars.put(rootWidgetInfo.className); 947 codeVars.put(" "); 948 codeVars.put(rootWidgetInfo.name); 949 codeVars.put(")\n"); 950 codeVars.put(" {\n"); 951 codeVars.put(codeRetranslate.data); 952 codeVars.put(" } // retranslateUi\n"); 953 return codeVars.data; 954 } 955 } 956 957 string generateUICode()(string xml, string customWidgetPackage = "") 958 { 959 return UICodeWriter!()().convert(xml, customWidgetPackage); 960 } 961 962 struct UIStruct(string filename, string customWidgetPackage = "") 963 { 964 mixin(generateUICode(import(filename), customWidgetPackage)); 965 }