1 module sbylib.event.event; 2 3 import std; 4 import sbylib.wrapper.glfw; 5 import sbylib.event; 6 7 interface IEvent { 8 void kill(); 9 void addFinishCallback(void delegate()); 10 bool isAlive() const; 11 EventContext[] context(); 12 } 13 14 class Event(Args...) : IEvent { 15 private void delegate(Args) callback; 16 private void delegate(Exception) onerror; 17 public bool delegate() killCondition; 18 private void delegate()[] finishCallbackList; 19 package EventContext[] _context; 20 private bool alive = true; 21 22 this() { 23 foreach (c; EventContext.currentContext) { 24 _context ~= c; 25 } 26 foreach (c; context) { 27 c.eventList ~= this; 28 } 29 EventWatcher._eventList ~= this; 30 } 31 32 void fire(Args args) { 33 try { 34 if (this.isAlive is false) return; 35 if (killCondition && killCondition()) { 36 this.alive = false; 37 foreach (cb; finishCallbackList) cb(); 38 return; 39 } 40 if (context.any!(c => c.isBound is false)) return; 41 if (callback) callback(args); 42 } catch (Throwable e) { 43 if (context.empty) { 44 this.kill(); 45 } else { 46 assert(context.all!(c => c.eventList[].canFind(this))); // for destroy myself 47 context.each!(c => c.destroy()); 48 } 49 assert(this.killCondition !is null && this.killCondition()); // to ensure this event is already dead. 50 throw e; 51 } 52 } 53 54 void fireOnce(Args args) { 55 this.fire(args); 56 this.kill(); 57 this.fire(args); // for launch finish callback 58 } 59 60 void throwError(Exception e) { 61 this.kill(); 62 if (onerror) onerror(e); 63 else throw e; 64 } 65 66 override bool isAlive() const { 67 return alive; 68 } 69 70 override void kill() { 71 this.killCondition = () => true; 72 } 73 74 override void addFinishCallback(void delegate() finishCallback) { 75 if (this.isAlive) { 76 this.finishCallbackList ~= finishCallback; 77 } else { 78 finishCallback(); 79 } 80 } 81 82 override EventContext[] context() { 83 return _context; 84 } 85 86 static Event create(Args...)(Event!Args delegate(void delegate()) cb) { 87 auto event = new Event; 88 cb(&event.fire).ownedBy(event); 89 return event; 90 } 91 } 92 93 Event!(Args) then(Args...)(Event!(Args) event, void delegate() callback) 94 if (Args.length > 0) 95 { 96 assert(event.callback is null); 97 event.callback = (Args _) { callback(); }; 98 return event; 99 } 100 101 Event!(Args) then(Args...)(Event!(Args) event, void delegate(Args) callback) { 102 assert(event.callback is null); 103 event.callback = callback; 104 return event; 105 } 106 107 Event!(Args) error(Args...)(Event!(Args) event, void delegate(Exception) callback) { 108 assert(event.onerror is null); 109 event.onerror = callback; 110 return event; 111 } 112 113 Event!(Args) until(Args...)(Event!(Args) event, bool delegate() condition) { 114 assert(event.killCondition is null); 115 event.killCondition = condition; 116 return event; 117 } 118 119 Event!(Args) once(Args...)(Event!(Args) event) { 120 bool hasRun; 121 event.killCondition = { 122 if (hasRun) return true; 123 hasRun = true; 124 return false; 125 }; 126 return event; 127 } 128 129 Event!(Args) once(Args...)(Event!(Args) event, void delegate() callback) 130 if (Args.length > 0) 131 { 132 return event.then(callback).once; 133 } 134 135 Event!(Args) once(Args...)(Event!(Args) event, void delegate(Args) callback) { 136 return event.then(callback).once; 137 } 138 139 Event!(Args) ownedBy(Args...)(Event!(Args) event, IEvent owner) { 140 return event.until(() => !owner.isAlive); 141 } 142 143 alias VoidEvent = Event!(); 144 145 class EventWatcher { 146 static: 147 148 Array!IEvent _eventList; 149 150 void update() { 151 _eventList.linearRemove(_eventList[].find!(e => !e.isAlive)); 152 } 153 154 const(Array!IEvent) eventList() { 155 return _eventList; 156 } 157 158 } 159 160 class EventException : Exception { 161 mixin basicExceptionCtors; 162 }