1 /++ 2 A small extension module to [arsd.minigui] that adds 3 functions for creating widgets and windows from short 4 XML descriptions. 5 6 If you choose to use this, it will require [arsd.dom] 7 to be compiled into your project too. 8 9 --- 10 import arsd.minigui_xml; 11 Window window = createWindowFromXml(` 12 <MainWindow> 13 <Button label="Hi!" /> 14 </MainWindow> 15 `); 16 --- 17 18 19 To add custom widgets to the minigui_xml factory, you need 20 to register them with FIXME. 21 22 You can attach some events right in the XML using attributes. 23 The attribute names are `onEVENTNAME` or `ondirectEVENTNAME` 24 and the values are one of the following three value types: 25 26 $(LIST 27 * If it starts with `&`, it is a delegate you need 28 to register using the FIXME function. 29 30 * If it starts with `(`, it is a string passed to 31 the [arsd.dom.querySelector] function to get an 32 element reference 33 34 * Otherwise, it tries to call a script function (if 35 scripting is available). 36 ) 37 38 Keep in mind 39 For example, to make a page widget that changes based on a 40 drop down selection, you may: 41 42 ```xml 43 <DropDownSelection onchange="$(+PageWidget).setCurrentTab"> 44 <option>Foo</option> 45 <option>Bar</option> 46 </DropDownSelection> 47 <PageWidget name="mypage"> 48 <!-- contents elided --> 49 </PageWidget> 50 ``` 51 52 That will create a select widget that when it changes, it will 53 look for the next PageWidget sibling (that's the meaning of `+PageWidget`, 54 see css selector syntax for more) and call its `setCurrentTab` 55 method. 56 57 Since the function knows `setCurrentTab` takes an integer, it will 58 automatically pull the `intValue` member out of the event and pass 59 it to the method. 60 61 The given XML is the same as the following D: 62 63 --- 64 auto select = new DropDownSelection(parent); 65 select.addOption("Foo"); 66 select.addOption("Bar"); 67 auto page = new PageWidget(parent); 68 page.name = "mypage"; 69 70 select.addEventListener("change", (Event event) { 71 page.setCurrentTab(event.intValue); 72 }); 73 --- 74 +/ 75 module arsd.minigui_xml; 76 77 public import arsd.minigui; 78 79 import arsd.dom; 80 81 private template ident(T...) { 82 static if(is(T[0])) 83 alias ident = T[0]; 84 else 85 alias ident = void; 86 } 87 88 private 89 Widget delegate(string[string] args, Widget parent)[string] widgetFactoryFunctions; 90 91 private 92 void loadMiniguiPublicClasses() { 93 if(widgetFactoryFunctions !is null) 94 return; 95 96 import std.traits; 97 import std.conv; 98 99 foreach(memberName; __traits(allMembers, mixin("arsd.minigui"))) static if(__traits(compiles, __traits(getMember, mixin("arsd.minigui"), memberName))) { 100 alias Member = ident!(__traits(getMember, mixin("arsd.minigui"), memberName)); 101 static if(is(Member == class) && !isAbstractClass!Member && is(Member : Widget) && __traits(getProtection, Member) != "private") { 102 widgetFactoryFunctions[memberName] = (string[string] args, Widget parent) { 103 static if(is(Member : Dialog)) { 104 return new Member(); 105 } else static if(is(Member : Window)) { 106 return new Member("test"); 107 } else { 108 auto paramNames = ParameterIdentifierTuple!(__traits(getMember, Member, "__ctor")); 109 Parameters!(__traits(getMember, Member, "__ctor")) params; 110 111 foreach(idx, param; params[0 .. $-1]) { 112 if(auto arg = paramNames[idx] in args) { 113 static if(is(typeof(param) == MemoryImage)) { 114 115 } else static if(is(typeof(param) == Color)) { 116 params[idx] = Color.fromString(*arg); 117 } else 118 params[idx] = to!(typeof(param))(*arg); 119 } 120 } 121 122 params[$-1] = parent; 123 124 auto widget = new Member(params); 125 126 if(auto st = "statusTip" in args) 127 widget.statusTip = *st; 128 if(auto st = "name" in args) 129 widget.name = *st; 130 return widget; 131 } 132 }; 133 } 134 } 135 } 136 137 /// 138 Widget makeWidgetFromString(string xml, Widget parent) { 139 auto document = new Document(xml, true, true); 140 auto r = document.root; 141 return miniguiWidgetFromXml(r, parent); 142 } 143 144 /// 145 Window createWindowFromXml(string xml) { 146 return createWindowFromXml(new Document(xml, true, true)); 147 } 148 /// 149 Window createWindowFromXml(Document document) { 150 auto r = document.root; 151 return cast(Window) miniguiWidgetFromXml(r, null); 152 } 153 /// 154 Widget miniguiWidgetFromXml(Element element, Widget parent) { 155 if(widgetFactoryFunctions is null) 156 loadMiniguiPublicClasses(); 157 if(auto factory = element.tagName in widgetFactoryFunctions) { 158 auto p = (*factory)(element.attributes, parent); 159 foreach(child; element.children) 160 if(child.tagName != "#text") 161 miniguiWidgetFromXml(child, p); 162 return p; 163 } else { 164 import std.stdio; 165 writeln("Unknown class: ", element.tagName); 166 return null; 167 } 168 } 169 170