1 module glui.slot;
2 
3 import raylib;
4 
5 import std.traits;
6 
7 import glui.node;
8 import glui.utils;
9 import glui.style;
10 import glui.structs;
11 
12 
13 @safe:
14 
15 
16 /// Quickly create a node slot.
17 alias nodeSlot(alias T) = simpleConstructor!(GluiNodeSlot, T);
18 
19 /// A "node slot" node, which displays the node given to it. Allows safely swapping nodes in the layout by reference,
20 /// even during drawing.
21 class GluiNodeSlot(T : GluiNode) : GluiNode {
22 
23     /// GluiNodeSlot defines its own styles, which will only apply to the slot itself, not the contents. Most of the
24     /// styling options will have no effect, but padding and margin will.
25     mixin DefineStyles;
26 
27     public {
28 
29         /// Node placed in the slot.
30         T value;
31 
32         /// If true, the slot will inherit its layout from the node it holds. If there's no node, it'll be reset to
33         /// shrink.
34         ///
35         /// Note: This field is stupid. You'd rather set the child to use `.layout!"fill"`.
36         bool inheritLayout;
37 
38     }
39 
40     /// Create a new slot and place a node in it.
41     this(Layout layout, T node) {
42 
43         this.value = node;
44         this.layout = layout;
45 
46     }
47 
48     this(T node) {
49 
50         this.value = node;
51         updateLayout();
52 
53     }
54 
55     /// Create a new empty slot.
56     this(Layout layout = Layout.init) {
57 
58         this.layout = layout;
59 
60     }
61 
62     /// Change the node in the slot.
63     ///
64     /// This function is a little bit more convenient than setting the value directly, as it'll mark itself as
65     /// needing an update. Additionally, it returns the slot, not the given node, so you can assign a value to a
66     /// constructed slot while adding it to the scene tree.
67     typeof(this) opAssign(T value) {
68 
69         updateSize();
70 
71         this.value = value;
72 
73         return this;
74 
75     }
76 
77     protected override void resizeImpl(Vector2 space) {
78 
79         if (!value) return;
80 
81         updateLayout();
82 
83         value.resize(tree, theme, space);
84         minSize = value.minSize;
85 
86     }
87 
88     protected override void drawImpl(Rectangle paddingBox, Rectangle contentBox) {
89 
90         if (!value) return;
91 
92         updateLayout();
93 
94         // The value is to be removed
95         if (value.toRemove) {
96 
97             // Clear the value
98             value = null;
99 
100         }
101 
102         // Draw as expected
103         else value.draw(contentBox);
104 
105     }
106 
107     protected override bool hoveredImpl(Rectangle rect, Vector2 position) const {
108 
109         if (!value) return false;
110 
111         // If the child has ignoreMouse set, we should ignore it as well
112         if (value.ignoreMouse) return false;
113 
114         // hoveredImpl may be private... uhhh
115         return (cast(const GluiNode) value).hoveredImpl(rect, position);
116 
117     }
118 
119     override const(Style) pickStyle() const {
120 
121         return style;
122 
123     }
124 
125     /// Swap contents of the two slots.
126     void swapSlots(Slot : GluiNode)(Slot other) {
127 
128         static if (is(Slot : GluiNodeSlot!U, U)) {
129 
130             import std.format;
131             import std.algorithm;
132 
133             updateSize();
134 
135             static if (is(T == U)) {
136 
137                 swap(value, other.value);
138 
139             }
140 
141             else static if (is(T : U) || is(U : T)) {
142 
143                 auto theirs = cast(T) other.value;
144                 auto ours   = cast(U) value;
145 
146                 const canAcceptTheirs = theirs || other.value is null;
147                 const canAcceptOurs   = ours   || value is null;
148 
149                 assert(canAcceptTheirs, format!"Can't swap: This slot doesn't accept %s"(typeid(theirs)));
150                 assert(canAcceptOurs,   format!"Can't swap: Other slot doesn't accept %s"(typeid(ours)));
151 
152                 // Perform the swap
153                 value = theirs;
154                 other.value = ours;
155 
156             }
157 
158             else static assert(false, "Slots given to swapSlots are not compatible");
159 
160         }
161 
162         else static assert(false, "The other item is not a node");
163 
164     }
165 
166     private void updateLayout() {
167 
168         assert(value);
169 
170         if (inheritLayout) {
171 
172             layout = value.layout;
173 
174         }
175 
176     }
177 
178 }