1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source 6 * Documentation: 7 * Coverage: 8 */ 9 module liberty.core.scenegraph.services; 10 /// 11 immutable NodeServices = q{ 12 this(string id, Node parent = CoreEngine.get.activeScene.tree) { 13 if (parent is null) { 14 assert(0, "Parent object cannot be null"); 15 } 16 super(id, parent); 17 import std.traits: hasUDA; 18 enum finalClass = __traits(isFinalClass, this); 19 enum abstractClass = __traits(isAbstractClass, this); 20 static if (!(finalClass || abstractClass)) { 21 static assert(0, "A node object class must either be final or abstract!"); 22 } 23 static foreach (i, member; __traits(derivedMembers, typeof(this))) { 24 static if (mixin("hasUDA!(" ~ typeof(this).stringof ~ "." ~ member ~ ", Constructor)")) { 25 enum accessFun = __traits(getProtection, __traits(getMember, this, member)); 26 enum finalFun = __traits(isFinalFunction, __traits(getMember, this, member)); 27 enum abstractFun = __traits(isAbstractFunction, __traits(getMember, this, member)); 28 static if (accessFun == "private" || accessFun == "protected") { 29 static if ((!finalFun || finalClass) && !abstractFun) { 30 static if (member.stringof == "\"_\"") { 31 mixin(member ~ "();"); 32 } else { 33 static assert(0, "NodeObject's constructor must be named '_'!"); 34 } 35 } else { 36 static assert(0, "NodeObject's constructor cannot be final or abstract!"); 37 } 38 } else { 39 static assert(0, "NodeObject's constructor must be private or protected!"); 40 } 41 } 42 } 43 static if (__traits(isFinalClass, this)) { 44 static foreach (el; ["start", "update", "process", "render"]) { 45 static foreach (super_member; __traits(derivedMembers, typeof(super))) { 46 static if (super_member.stringof == "\"" ~ el ~ "\"") { 47 mixin("scene." ~ el ~ "List[id] = this;"); 48 } 49 } 50 static foreach (member; __traits(derivedMembers, typeof(this))) { 51 static if (member.stringof == "\"" ~ el ~ "\"") { 52 mixin("scene." ~ el ~ "List[id] = this;"); 53 } 54 } 55 } 56 } 57 } 58 }; 59 /// 60 immutable ListenerServices = q{ 61 /// Starts current containing events and @Signal events. 62 /// A @Signal event overrides a current containing event. 63 void startListening(T)(ref T element) { 64 import std.traits: hasUDA, getUDAs; 65 if (!element.canListen()) { 66 element.__canListen(true); 67 if (typeof(element).stringof == Button.stringof) { 68 mixin(ButtonListenerServices); 69 } 70 } 71 } 72 /// Clears all events. Restarts only @Signal events. 73 void restartListening(T)(ref T element) { 74 import std.traits: hasUDA, getUDAs; 75 element.stopListening(); 76 element.__canListen(true); 77 if (typeof(element).stringof == Button.stringof) { 78 mixin(ButtonListenerServices); 79 } 80 } 81 }; 82 /// 83 immutable ButtonListenerServices = q{ 84 static foreach (member; __traits(derivedMembers, typeof(this))) { 85 static if (typeof(super).stringof == "Canvas") { 86 static if (mixin("hasUDA!(" ~ typeof(this).stringof ~ "." ~ member ~ ", Signal)")) { 87 static foreach (el; ["RightClick", "LeftClick", "MouseMove", "MouseInside", "Update"]) { 88 static if (member.stringof == "\"on" ~ el ~ getUDAs!(__traits(getMember, this, member), Signal)[0].id ~ "\"") { 89 if (element.id == getUDAs!(__traits(getMember, this, member), Signal)[0].id) { 90 mixin("element.on" ~ el ~ " = &on" ~ el ~ getUDAs!(__traits(getMember, this, member), Signal)[0].id ~ ";"); 91 } 92 } 93 } 94 } 95 } 96 } 97 }; 98 /// 99 struct Constructor; 100 /// 101 struct Signal { 102 string id; 103 } 104 /// 105 interface Startable { 106 /// 107 void start(); 108 } 109 /// 110 interface Updatable { 111 /// 112 void update(in float deltaTime); 113 } 114 /// 115 interface Processable { 116 /// 117 void process(); 118 }