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 }