1 /// Implementation of `OverlayIO` using its own space to lay out its children.2 modulefluid.overlay_chain;
3 4 importfluid.node;
5 importfluid.utils;
6 importfluid.types;
7 importfluid.children;
8 importfluid.node_chain;
9 10 importfluid.io.overlay;
11 12 @safe:
13 14 /// The usual node builder for `OverlayChain`.15 aliasoverlayChain = nodeBuilder!OverlayChain;
16 17 /// This node implements the `OverlayIO` interface by using its own space to lay out its children.18 ///19 /// As this node is based on `NodeChain`, it accepts a single regular node to draw inside. It will always be drawn20 /// first, before the overlay nodes. The usual, "regular" content can thus be placed as a regular child, and overlays21 /// can then be spawned and be drawn above.22 ///23 /// The `OverlayChain` can be considered a more modern alternative to `MapFrame`, however it is not guaranteed24 /// to be compatible with the old backend. For future code, `OverlayChain` should generally be preferred, but it has25 /// some drawbacks:26 ///27 /// * Overlay nodes drawn on `OverlayChain` must implement `Overlayable`.28 /// * `OverlayChain` is not a `Frame`, and cannot be used as one.29 /// * It is not stylable; background color, border or decorations cannot be used, and margins may not work.30 /// * Overlays are not considered in the chain's `minSize`.31 /// * Position is relative to the window, not to the node.32 classOverlayChain : NodeChain, OverlayIO {
33 34 mixincontrolIO;
35 36 protectedstructChild {
37 Nodenode;
38 Overlayableoverlayable;
39 Overlayableparent;
40 }
41 42 protected {
43 44 /// Overlay nodes of the chain.45 Child[] children;
46 47 }
48 49 this(Nodenext = null) {
50 super(next);
51 }
52 53 overridevoidbeforeResize(Vector2) {
54 startIO();
55 }
56 57 overridevoidafterResize(Vector2space) {
58 foreach (child; children) {
59 resizeChild(child.node, space);
60 }
61 stopIO();
62 }
63 64 overridevoidafterDraw(Rectangle, Rectangleinner) {
65 size_tnewIndex;
66 foreach (child; children) {
67 68 // Filter removed nodes out69 if (child.node.toRemove) {
70 child.node.toRemove = false;
71 continue;
72 }
73 scope (exit) children[newIndex++] = child;
74 75 constsize = child.node.minSize;
76 constnodeAlign = child.node.layout.nodeAlign;
77 78 // Calculate the node's position based on the anchor79 constanchor = child.overlayable.getAnchor(inner);
80 constlayout = Vector2(
81 alignLayout!'x'(nodeAlign[0], inner, anchor, size),
82 alignLayout!'y'(nodeAlign[1], inner, anchor, size),
83 );
84 constanchorPoint = anchor.end85 - Vector2(layout.x * anchor.size.x, layout.y * anchor.size.y)
86 - Vector2(layout.x * size.x, layout.y * size.y);
87 88 drawChild(child.node, Rectangle(
89 anchorPoint.tupleof,
90 size.tupleof,
91 ));
92 93 }
94 children.length = newIndex;
95 }
96 97 privatestaticalignLayout(charaxis)(NodeAlignalignment, Rectangleinner, Rectangleanchor, Vector2size) {
98 99 importstd.algorithm : predSwitch;
100 101 if (alignment == NodeAlign.fill) {
102 103 // +---- inner ---+104 // | . .| <- end |105 // |. . | space |106 // +----+====+ |107 // | | . .| |108 // | |. . | <- anchor109 // | +====+----+110 // | start | . .|111 // | space-> |. . |112 // +---------+----+113 114 conststartSpace = inner.end - anchor.end;
115 constendSpace = anchor.start - inner.start;
116 117 staticif (axis == 'x') {
118 if (size.x <= startSpace.x) return0;
119 if (size.x <= endSpace.x) return1;
120 return0.5;
121 }
122 123 staticif (axis == 'y') {
124 if (size.y <= startSpace.y) return0;
125 if (size.y <= endSpace.y) return1;
126 return0.5;
127 }
128 129 }
130 131 elsereturnalignment.predSwitch(
132 NodeAlign.start, 0,
133 NodeAlign.center, 0.5,
134 NodeAlign.end, 1,
135 );
136 137 }
138 139 overridevoidaddOverlay(Overlayableoverlayable, OverlayType[] type...) nothrow {
140 141 autonode = cast(Node) overlayable;
142 assert(node, "Given overlay is not a Node");
143 144 children ~= Child(node, overlayable);
145 updateSize();
146 147 // Technically, updateSize could be avoided by resizing nothing but the newly added overlay,148 // and storing the relevant I/O systems. Right now, I find this approach easier.149 150 }
151 152 overridevoidaddChildOverlay(Overlayableparent, Overlayableoverlayable, OverlayType[] type...) nothrow {
153 154 autonode = cast(Node) overlayable;
155 assert(node, "Given overlay is not a Node");
156 157 children ~= Child(node, overlayable, parent);
158 updateSize();
159 160 }
161 162 }