1 /**
2 Copyright: Copyright (c) 2013-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.eventpropagators;
8 
9 import std.traits;
10 import anchovy.gui;
11 
12 ///
13 enum PropagatingStrategy
14 {
15 	/// First visits parent then child until target is reached.
16 	/// Then sets event's bubbling flag to true and visits widgets from
17 	/// target to root.
18 	SinkBubble,
19 
20 	/// First visits target then its parent etc, until reaches root.
21 	/// Then sets event's sinking flag to true and visits widgets from root to target.
22 	BubbleSink,
23 
24 	/// Visit all the subtree from bottom up. parent gets visited after all its children was visited.
25 	/// Also called Pre-order.
26 	ChildrenFirst,
27 
28 	/// Visits all subtree from root to leafs, visiting parent first and then all its subtrees. Depth-first.
29 	/// Also called Post-order.
30 	ParentFirst
31 }
32 
33 enum OnHandle
34 {
35 	StopTraversing,
36 	ContinueTraversing
37 }
38 
39 Widget[] propagateEventSinkBubble(OnHandle onHandle = OnHandle.StopTraversing)(Widget[] widgets, Event event)
40 {
41 	// Phase 1: event sinking to target.
42 	event.sinking = true;
43 
44 	foreach(index, widget; widgets)
45 	{
46 		event.handled = event.handled || widget.handleEvent(event);
47 
48 		static if(onHandle == OnHandle.StopTraversing)
49 		{
50 			if (event.handled) return widgets[0..index+1];
51 		}
52 	}
53 
54 	// Phase 2: event bubling from target.
55 	event.bubbling = true;
56 	foreach_reverse(index, widget; widgets)
57 	{
58 		event.handled = event.handled || widget.handleEvent(event);
59 
60 		static if(onHandle == OnHandle.StopTraversing)
61 		{
62 			if (event.handled) return widgets[0..index+1];
63 		}
64 	}
65 
66 	return [];
67 }
68 
69 void propagateEventParentFirst(Widget root, Event event)
70 {
71 	event.sinking = true;
72 
73 	void propagateEvent(Widget root)
74 	{
75 		root.handleEvent(event);
76 
77 		foreach(child; root.getPropertyAs!("children", Widget[]))
78 		{
79 			propagateEvent(child);
80 		}
81 	}
82 
83 	propagateEvent(root);
84 }
85 
86 void propagateEventSinkBubbleTree(Widget root, Event e)
87 {
88 	e.sinking = true;
89 	root.handleEvent(e);
90 
91 	foreach (widget; root.getPropertyAs!("children", Widget[]))
92 	{
93 		e.sinking = true;
94 		widget.propagateEventSinkBubbleTree(e);
95 	}
96 
97 	e.bubbling = true;
98 	root.handleEvent(e);
99 }
100 
101 void propagateEventChildrenFirst(Widget root, Event event)
102 {
103 	event.bubbling = true;
104 
105 	void propagateEvent(Widget root)
106 	{
107 		foreach(child; root.getPropertyAs!("children", Widget[]))
108 		{
109 			propagateEvent(child);
110 		}
111 
112 		root.handleEvent(event);
113 	}
114 
115 	propagateEvent(root);
116 }
117 
118 /// Tests all root's children with pred.
119 /// Then calls itself with found child.
120 /// Adds widgets satisfying pred to returned array.
121 /// Root widget is added first.
122 /// Can be used to find widget that is under cursor
123 /// Parameters:
124 ///   pred function like bool fun(Widget widget, ...)
125 ///   root root of widget subtree/tree
126 Widget[] buildPathToLeaf(alias pred, T...)(Widget root, T data)
127 {
128 	Widget[] path;
129 
130 	bool traverse(Widget root)
131 	{
132 		if(!pred(root, data)) return false;
133 
134 		path ~= root;
135 
136 		foreach(child; root.getPropertyAs!("children", Widget[]))
137 		{
138 			if (traverse(child))
139 			{
140 				return true;
141 			}
142 		}
143 
144 		return true;
145 	}
146 
147 	traverse(root);
148 
149 	return path;
150 }