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 }