1 /* 2 Copyright (c) 2013 Andrey Penechko 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license the "Software" to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module anchovy.gui.eventpropagators; 30 31 import std.traits; 32 import anchovy.gui; 33 34 /// 35 enum PropagatingStrategy 36 { 37 /// First visits parent then child until target is reached. 38 /// Then sets event's bubbling flag to true and visits widgets from 39 /// target to root. 40 SinkBubble, 41 42 /// First visits target then its parent etc, until reaches root. 43 /// Then sets event's sinking flag to true and visits widgets from root to target. 44 BubbleSink, 45 46 /// Visit all the subtree from bottom up. parent gets visited after all its children was visited. 47 /// Also called Pre-order. 48 ChildrenFirst, 49 50 /// Visits all subtree from root to leafs, visiting parent first and then all its subtrees. Depth-first. 51 /// Also called Post-order. 52 ParentFirst 53 } 54 55 enum OnHandle 56 { 57 StopTraversing, 58 ContinueTraversing 59 } 60 61 Widget[] propagateEventSinkBubble(OnHandle onHandle = OnHandle.StopTraversing)(Widget[] widgets, Event event) 62 { 63 // Phase 1: event sinking to target. 64 event.sinking = true; 65 66 foreach(index, widget; widgets) 67 { 68 event.handled = event.handled || widget.handleEvent(event); 69 70 static if(onHandle == OnHandle.StopTraversing) 71 { 72 if (event.handled) return widgets[0..index+1]; 73 } 74 } 75 76 // Phase 2: event bubling from target. 77 event.bubbling = true; 78 foreach_reverse(index, widget; widgets) 79 { 80 event.handled = event.handled || widget.handleEvent(event); 81 82 static if(onHandle == OnHandle.StopTraversing) 83 { 84 if (event.handled) return widgets[0..index+1]; 85 } 86 } 87 88 return []; 89 } 90 91 void propagateEventParentFirst(Widget root, Event event) 92 { 93 event.sinking = true; 94 95 void propagateEvent(Widget root) 96 { 97 root.handleEvent(event); 98 99 foreach(child; root.getPropertyAs!("children", Widget[])) 100 { 101 propagateEvent(child); 102 } 103 } 104 105 propagateEvent(root); 106 } 107 108 void propagateEventSinkBubbleTree(Widget root, Event e) 109 { 110 e.sinking = true; 111 root.handleEvent(e); 112 113 foreach (widget; root.getPropertyAs!("children", Widget[])) 114 { 115 e.sinking = true; 116 widget.propagateEventSinkBubbleTree(e); 117 } 118 119 e.bubbling = true; 120 root.handleEvent(e); 121 } 122 123 void propagateEventChildrenFirst(Widget root, Event event) 124 { 125 event.bubbling = true; 126 127 void propagateEvent(Widget root) 128 { 129 foreach(child; root.getPropertyAs!("children", Widget[])) 130 { 131 propagateEvent(child); 132 } 133 134 root.handleEvent(event); 135 } 136 137 propagateEvent(root); 138 } 139 140 /// Tests all root's children with pred. 141 /// Then calls itself with found child. 142 /// Adds widgets satisfying pred to returned array. 143 /// Root widget is added first. 144 /// Can be used to find widget that is under cursor 145 /// Parameters: 146 /// pred function like bool fun(Widget widget, ...) 147 /// root root of widget subtree/tree 148 Widget[] buildPathToLeaf(alias pred, T...)(Widget root, T data) 149 { 150 Widget[] path; 151 152 bool traverse(Widget root) 153 { 154 if(!pred(root, data)) return false; 155 156 path ~= root; 157 158 foreach(child; root.getPropertyAs!("children", Widget[])) 159 { 160 if (traverse(child)) 161 { 162 return true; 163 } 164 } 165 166 return true; 167 } 168 169 traverse(root); 170 171 return path; 172 }