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 }