1 /// 2 module fluid.utils; 3 4 import std.meta; 5 import std.functional; 6 7 import fluid.backend; 8 9 10 @safe: 11 12 13 // For saner testing and debugging. 14 version (unittest) 15 private extern(C) __gshared string[] rt_options = ["oncycle=ignore"]; 16 17 /// Create a simple node constructor for declarative usage. 18 /// 19 /// Initial properties can be provided in the function provided in the second argument. 20 enum simpleConstructor(T, alias fun = "a") = SimpleConstructor!(T, fun).init; 21 22 /// Create a simple template node constructor for declarative usage. 23 /// 24 /// If the parent is a simple constructor, its initializer will be ran *after* this one. This is because the user 25 /// usually specifies the parent in templates, so it has more importance. 26 /// 27 /// T must be a template accepting a single parameter — Parent type will be passed to it. 28 template simpleConstructor(alias T, alias Parent, alias fun = "a") { 29 30 alias simpleConstructor = simpleConstructor!(T!(Parent.Type), (a) { 31 32 alias initializer = unaryFun!fun; 33 34 initializer(a); 35 Parent.initializer(a); 36 37 }); 38 39 } 40 41 /// ditto 42 alias simpleConstructor(alias T, Parent, alias fun = "a") = simpleConstructor!(T!Parent, fun); 43 44 enum isSimpleConstructor(T) = is(T : SimpleConstructor!(A, a), A, alias a); 45 46 struct SimpleConstructor(T, alias fun = "a") { 47 48 alias Type = T; 49 alias initializer = unaryFun!fun; 50 51 Type opCall(Args...)(Args args) { 52 53 import fluid.style; 54 import fluid.structs; 55 56 // Determine if an argument is a parameter 57 enum isTheme(T) = is(T : Theme); 58 enum isLayout(T) = is(T : Layout); 59 60 // Scan for parameters 61 static if (Args.length >= 1) { 62 63 alias FirstArg = Args[0]; 64 65 // Load the second argument with a fallback 66 static if (Args.length >= 2) 67 alias SecondArg = Args[1]; 68 else 69 alias SecondArg = void; 70 71 // Check if the first parameter is a parameter 72 static if (isTheme!FirstArg) { 73 74 enum arity = 1 + isLayout!SecondArg; 75 76 } 77 78 else static if (isLayout!FirstArg) { 79 80 enum arity = 1 + isTheme!SecondArg; 81 82 } 83 84 else enum arity = 0; 85 86 } 87 88 else enum arity = 0; 89 90 // Construct the node 91 auto params = NodeParams(args[0..arity]); 92 93 // Collect the parameters into NodeParams 94 static if (__traits(compiles, new Type(params, args[arity..$]))) { 95 96 auto result = new Type(params, args[arity..$]); 97 98 } 99 100 // Old-style, plain construction 101 else static if (__traits(compiles, new Type(args))) { 102 103 auto result = new Type(args); 104 105 } 106 107 // If neither compile, try the new call convention again to make sure it emits an error message 108 else auto result = new Type(params, args[arity..$]); 109 110 initializer(result); 111 return result; 112 113 } 114 115 } 116 117 unittest { 118 119 static class Foo { 120 121 string value; 122 123 this() { } 124 125 } 126 127 alias xfoo = simpleConstructor!Foo; 128 assert(xfoo().value == ""); 129 130 alias yfoo = simpleConstructor!(Foo, (a) { 131 a.value = "foo"; 132 }); 133 assert(yfoo().value == "foo"); 134 135 auto myFoo = new Foo; 136 yfoo.initializer(myFoo); 137 assert(myFoo.value == "foo"); 138 139 static class Bar(T) : T { 140 141 int foo; 142 143 this(int foo) { 144 145 this.foo = foo; 146 147 } 148 149 } 150 151 alias xbar(alias T) = simpleConstructor!(Bar, T); 152 153 const barA = xbar!Foo(1); 154 assert(barA.value == ""); 155 assert(barA.foo == 1); 156 157 const barB = xbar!xfoo(2); 158 assert(barB.value == ""); 159 assert(barB.foo == 2); 160 161 const barC = xbar!yfoo(3); 162 assert(barC.value == "foo"); 163 assert(barC.foo == 3); 164 165 } 166 167 deprecated("BasicNodeParams are deprecated in favor of simpleConstructor. Define constructors using NodeParams as the " 168 ~ "first argument instead") { 169 170 alias BasicNodeParamLength = Alias!5; 171 template BasicNodeParam(int index) { 172 173 import fluid.style; 174 import fluid.structs; 175 176 static if (index == 0) alias BasicNodeParam = AliasSeq!(Layout, Theme); 177 static if (index == 1) alias BasicNodeParam = AliasSeq!(Theme, Layout); 178 static if (index == 2) alias BasicNodeParam = AliasSeq!(Layout); 179 static if (index == 3) alias BasicNodeParam = AliasSeq!(Theme); 180 static if (index == 4) alias BasicNodeParam = AliasSeq!(); 181 182 } 183 184 } 185 186 /// Check if the rectangle contains a point. 187 bool contains(Rectangle rectangle, Vector2 point) { 188 189 return rectangle.x <= point.x 190 && point.x < rectangle.x + rectangle.width 191 && rectangle.y <= point.y 192 && point.y < rectangle.y + rectangle.height; 193 194 } 195 196 // Extremely useful Rectangle utilities 197 198 /// Get the top-left corner of a rectangle. 199 Vector2 start(Rectangle r) => Vector2(r.x, r.y); 200 201 /// Get the bottom-right corner of a rectangle. 202 Vector2 end(Rectangle r) => Vector2(r.x + r.w, r.y + r.h); 203 204 /// Get the center of a rectangle. 205 Vector2 center(Rectangle r) => Vector2(r.x + r.w/2, r.y + r.h/2); 206 207 /// Get the size of a rectangle. 208 Vector2 size(Rectangle r) => Vector2(r.w, r.h); 209 210 /// Get names of static fields in the given object. 211 /// 212 /// Ignores deprecated fields. 213 template StaticFieldNames(T) { 214 215 import std.traits : hasStaticMember; 216 import std.meta : Alias, Filter; 217 218 // Prepare data 219 alias Members = __traits(allMembers, T); 220 221 template isStaticMember(string member) { 222 223 enum isStaticMember = 224 225 // Make sure this isn't an alias 226 __traits(compiles, 227 Alias!(__traits(getMember, T, member)) 228 ) 229 230 && !__traits(isDeprecated, __traits(getMember, T, member)) 231 232 // Find the member 233 && hasStaticMember!(T, member); 234 235 } 236 237 // Result 238 alias StaticFieldNames = Filter!(isStaticMember, Members); 239 240 } 241 242 /// Open given URL in a web browser. 243 /// 244 /// Supports all major desktop operating systems. Does nothing if not supported on the given platform. 245 /// 246 /// At the moment this simply wraps `std.process.browse`. 247 void openURL(scope const(char)[] url) nothrow { 248 249 version (Posix) { 250 import std.process; 251 browse(url); 252 } 253 else version (Windows) { 254 import std.process; 255 browse(url); 256 } 257 258 // Do nothing on remaining platforms 259 260 }