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(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         bool inheritLayout;
35 
36     }
37 
38     /// Create a new slot and place a node in it.
39     this(Layout layout, T node) {
40 
41         this.value = node;
42         this.layout = layout;
43 
44     }
45 
46     this(T node) {
47 
48         this.value = node;
49         updateLayout();
50 
51     }
52 
53     /// Create a new empty slot.
54     this(Layout layout = Layout.init) {
55 
56         this.layout = layout;
57 
58     }
59 
60     /// Change the node in the slot.
61     ///
62     /// This function is a little bit more convenient than setting the value directly, as it'll mark itself as
63     /// needing an update. Additionally, it returns the slot, not the given node, so you can assign a value to a
64     /// constructed slot while adding it to the scene tree.
65     typeof(this) opAssign(T value) {
66 
67         updateSize();
68 
69         this.value = value;
70 
71         return this;
72 
73     }
74 
75     protected override void resizeImpl(Vector2 space) {
76 
77         if (!value) return;
78 
79         updateLayout();
80 
81         value.resize(tree, theme, space);
82         minSize = value.minSize;
83 
84     }
85 
86     protected override void drawImpl(Rectangle paddingBox, Rectangle contentBox) {
87 
88         if (!value) return;
89 
90         updateLayout();
91 
92         value.draw(contentBox);
93 
94     }
95 
96     protected override bool hoveredImpl(Rectangle rect, Vector2 position) const {
97 
98         if (!value) return false;
99 
100         // hoveredImpl may be private... uhhh
101         return (cast(const GluiNode) value).hoveredImpl(rect, position);
102 
103     }
104 
105     override const(Style) pickStyle() const {
106 
107         return style;
108 
109     }
110 
111     /// Swap contents of the two slots.
112     void swapSlots(Slot : GluiNode)(Slot other) {
113 
114         static if (is(Slot : GluiNodeSlot!U, U)) {
115 
116             import std.format;
117             import std.algorithm;
118 
119             updateSize();
120 
121             static if (is(T == U)) {
122 
123                 swap(value, other.value);
124 
125             }
126 
127             else static if (is(T : U) || is(U : T)) {
128 
129                 auto theirs = cast(T) other.value;
130                 auto ours   = cast(U) value;
131 
132                 const canAcceptTheirs = theirs || other.value is null;
133                 const canAcceptOurs   = ours   || value is null;
134 
135                 assert(canAcceptTheirs, format!"Can't swap: This slot doesn't accept %s"(typeid(theirs)));
136                 assert(canAcceptOurs,   format!"Can't swap: Other slot doesn't accept %s"(typeid(ours)));
137 
138                 // Perform the swap
139                 value = theirs;
140                 other.value = ours;
141 
142             }
143 
144             else static assert(false, "Slots given to swapSlots are not compatible");
145 
146         }
147 
148         else static assert(false, "The other item is not a node");
149 
150     }
151 
152     private void updateLayout() {
153 
154         assert(value);
155 
156         if (inheritLayout) {
157 
158             layout = value.layout;
159 
160         }
161 
162     }
163 
164 }