1 /** 2 Copyright: Copyright (c) 2014 Andrey Penechko. 3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module anchovy.gui.utils.widgetmanager; 8 9 import anchovy.gui; 10 11 //version = Debug_wman; 12 13 alias WidgetCreator = Widget delegate(); 14 alias LayoutCreator = ILayout delegate(); 15 alias BehaviorCreator = IWidgetBehavior delegate(); 16 17 struct WidgetManager 18 { 19 @disable this(); 20 21 this(GuiContext context) 22 { 23 _context = context; 24 } 25 26 /// Returns widget found by given id. 27 Widget getWidgetById(string id) 28 { 29 if (auto widget = id in _ids) 30 { 31 return *widget; 32 } 33 else 34 return null; 35 } 36 37 void setGroupSelected(int groupId, Widget widget) 38 { 39 auto wasSelectedPtr = groupId in _groupSelections; 40 Widget wasSelected = wasSelectedPtr ? *wasSelectedPtr : null; 41 42 _groupSelections[groupId] = widget; 43 44 if (wasSelected !is null) 45 { 46 wasSelected.handleEvent(new GroupSelectionEvent(widget)); 47 } 48 } 49 50 Widget getGroupSelected(uint groupId) 51 { 52 if (auto selection = groupId in _groupSelections) 53 { 54 return *selection; 55 } 56 57 return null; 58 } 59 60 Widget createWidget(string type, Widget parent = null) 61 { 62 return createWidgetImpl(type, parent); 63 } 64 65 private: 66 67 /// Stores widgets with id property. 68 Widget[string] _ids; 69 70 Widget[int] _groupSelections; 71 72 GuiContext _context; 73 74 //---------------------------- Helpers --------------------------------- 75 76 Widget createWidgetImpl(string type, Widget parent) 77 { 78 Widget widget; 79 IWidgetBehavior[] behaviors; 80 81 void attachBehaviorProperties(Widget _widget) 82 { 83 //----------------------- Attaching behaviors properties --------------------------- 84 85 if (auto factories = type in _context.behaviorFactories) 86 { 87 IWidgetBehavior behavior; 88 89 foreach(factory; *factories) 90 { 91 behavior = factory(); 92 behavior.attachPropertiesTo(_widget); 93 behaviors ~= behavior; 94 } 95 } 96 } 97 98 //----------------------- Instatiating templates --------------------------- 99 100 WidgetTemplate templ = _context.templateManager.getTemplate(type); 101 102 if (templ) 103 { 104 //----------------------- Base type construction ----------------------- 105 106 Widget baseWidget; 107 108 if (templ.baseType != "widget") 109 { 110 baseWidget = createWidget(templ.baseType); 111 } 112 else 113 { 114 baseWidget = createBaseWidget(type); // Create using factory. 115 attachBehaviorProperties(baseWidget); 116 } 117 118 baseWidget["context"] = _context; // widget may access context before construction ends. 119 baseWidget.setProperty!("subwidgets", Widget[string])(null); 120 121 //----------------------- Template construction ------------------------ 122 // Recursively creates widgets as stated in template. widget is root of that tree. 123 widget = createSubwidget(templ.tree, baseWidget, baseWidget); 124 125 if (templ.container) 126 { 127 auto subwidgets = widget.getPropertyAs!("subwidgets", Widget[string]); 128 auto container = subwidgets[templ.container]; 129 if (container) 130 { 131 version(Debug_wman) writefln("Adding container %s", templ.container); 132 widget["container"] = container; 133 } 134 } 135 136 widget["template"] = templ; 137 } 138 else 139 { 140 // if there is no template, lets create regular one. 141 widget = createBaseWidget(type); 142 attachBehaviorProperties(widget); 143 } 144 145 // Set given type, even if no such template exists. 146 // Helps to distinguish widgets. 147 widget["type"] = type; 148 149 // default style 150 if (widget["style"] == Variant(null)) 151 { 152 widget["style"] = type; 153 } 154 155 widget["context"] = _context; // if widget attempts to override context. 156 157 // adding parent 158 if (parent !is null) 159 { 160 addChild(parent, widget); 161 } 162 else 163 { 164 widget["parent"] = null; 165 } 166 167 //----------------------- Attaching behaviors --------------------------- 168 foreach(behavior; behaviors) 169 { 170 behavior.attachTo(widget); 171 } 172 173 widget["behaviors"] = behaviors; 174 175 return widget; 176 } 177 178 Widget createBaseWidget(string type) 179 { 180 if (auto factory = type in _context.widgetFactories) 181 { 182 return _context.widgetFactories[type](); 183 } 184 else 185 { 186 return new Widget; 187 } 188 } 189 190 import std.conv : parse; 191 import std..string : munch; 192 193 Variant parseProperty(string name, ref Variant value, Widget widget) 194 { 195 switch(name) 196 { 197 case "layout": 198 version(Debug_wman) writeln("found layout property: ", value.get!string); 199 if (auto factory = value.get!string in _context.layoutFactories) 200 { 201 auto result = Variant((*factory)()); 202 203 return result; 204 } 205 writefln("Error: unknown layout '%s' found", value.get!string); 206 break; 207 208 case "minSize", "prefSize", "position": 209 try 210 { 211 string nums = value.get!string; 212 int w = parse!int(nums); 213 munch(nums, " \t\n\r"); 214 int h = parse!int(nums); 215 return Variant(ivec2(w, h)); 216 } 217 catch (Exception e) 218 { 219 writefln("Error parsing %s %s from %s", name, e, value); 220 } 221 return Variant(ivec2(16, 16)); 222 223 case "id": 224 string id = value.get!string; 225 if (id in _ids) 226 { 227 writeln("Duplicate id found: ", id, ", overriding..."); 228 } 229 _ids[id] = widget; 230 231 return value; 232 233 default: 234 return value; 235 } 236 237 return value; 238 } 239 240 Widget createSubwidget(SubwidgetTemplate sub, Widget subwidget, Widget root) 241 { 242 //----------------------- Forwarding properties ------------------------ 243 foreach(forwardedProperty; sub.forwardedProperties) 244 { 245 version(Debug_wman) writefln("forwarding %s.%s to %s.%s", root["type"], forwardedProperty.propertyName, 246 subwidget["name"], forwardedProperty.targetPropertyName); 247 root[forwardedProperty.propertyName] = subwidget.property(forwardedProperty.targetPropertyName); 248 } 249 250 //------------------------ Assigning properties ------------------------ 251 foreach(propertyKey; sub.properties.byKey) 252 { 253 version(Debug_wman)writeln("new property ", propertyKey); 254 Variant value = parseProperty(propertyKey, sub.properties[propertyKey], subwidget); 255 version(Debug_wman)writeln("Parsed value ", value); 256 version(Debug_wman)writefln("Assigning properties %s %s %s %s %s %s", propertyKey, value, 257 subwidget["name"], subwidget["type"], root["name"], root["type"]); 258 subwidget[propertyKey] = value; 259 } 260 261 Variant* name = "name" in sub.properties; 262 if(name) 263 { 264 if (auto subtemplateName = name.peek!string) 265 { 266 Widget[string] subwidgets = root["subwidgets"].get!(Widget[string]); 267 subwidgets[*subtemplateName] = subwidget; 268 root["subwidgets"] = subwidgets; 269 } 270 } 271 272 // Attach subwidgets root widget. 273 subwidget["root"] = root; 274 275 //------------------------ Creating subwidgets ------------------------- 276 foreach(subtemplate; sub.subwidgets) 277 { 278 version(Debug_wman) writefln("%s: Adding subwidget %s", subwidget["name"], subtemplate.properties["type"].get!string); 279 createSubwidget(subtemplate, createWidget(subtemplate.properties["type"].get!string, subwidget), root); 280 } 281 282 return subwidget; 283 } 284 }