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 }