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 }