1 ///
2 module fluid.switch_slot;
3 
4 import fluid.node;
5 import fluid.utils;
6 import fluid.style;
7 import fluid.backend;
8 
9 
10 @safe:
11 
12 
13 /// A switch slot will try each of its children and pick the first one that fits the available space. If the a node
14 /// is too large to fit, it will try the next one in the list until it finds one that matches, or the last node in the
15 /// list.
16 ///
17 /// `null` is an acceptable value, indicating that no node should be drawn.
18 alias switchSlot = simpleConstructor!SwitchSlot;
19 
20 /// ditto
21 class SwitchSlot : Node {
22 
23     public {
24 
25         Node[] availableNodes;
26         Node node;
27 
28         /// If present, this node will only be drawn in case its principal node is hidden. In case the principal node is
29         /// another `SwitchSlot`, this might be because it failed to match any non-null node.
30         Node principalNode;
31 
32     }
33 
34     protected {
35 
36         /// Last available space assigned to this node.
37         Vector2 _availableSpace;
38 
39     }
40 
41     @property {
42 
43         alias isHidden = typeof(super).isHidden;
44 
45         override bool isHidden() const return {
46 
47             // Tree is available and resized
48             if (tree && !tree.resizePending) {
49 
50                 // Principal node is visible, hide self
51                 if (principalNode && !principalNode.isHidden)
52                     return true;
53 
54                 // Hide if no node was chosen
55                 return super.isHidden || node is null;
56 
57             }
58 
59             return super.isHidden;
60 
61         }
62 
63     }
64 
65     this(Node[] nodes...) {
66 
67         this.availableNodes ~= nodes;
68 
69     }
70 
71     /// Create a new slot that will only draw if this slot is hidden or ends up with a `null` node.
72     SwitchSlot retry(Args...)(Args args) {
73 
74         auto slot = switchSlot(args);
75         slot.principalNode = this;
76         return slot;
77 
78     }
79 
80     override void resizeImpl(Vector2 availableSpace) {
81 
82         minSize = Vector2();
83         this.node = null;
84         _availableSpace = availableSpace;
85 
86         // Try each option
87         foreach (i, node; availableNodes) {
88 
89             this.node = node;
90 
91             // Null node reached, stop with no minSize
92             if (node is null) return;
93 
94             auto previousTree = node.tree;
95             auto previousTheme = node.theme;
96             auto previousSize = node.minSize;
97 
98             resizeChild(node, availableSpace);
99 
100             // Stop if it fits within available space
101             if (node.minSize.x <= availableSpace.x && node.minSize.y <= availableSpace.y) break;
102 
103             // Restore previous info, unless this is the last node
104             if (i+1 != availableNodes.length && previousTree) {
105 
106                 // Resize the node again to recursively restore old parameters
107                 node.tree = null;
108                 node.inheritTheme(Theme.init);
109                 resizeChild(node, previousSize);
110 
111             }
112 
113         }
114 
115         // Copy minSize
116         minSize = node.minSize;
117 
118     }
119 
120     override void drawImpl(Rectangle outer, Rectangle inner) {
121 
122         // No node to draw, stop
123         if (node is null) return;
124 
125         // Draw the node
126         drawChild(node, inner);
127 
128     }
129 
130     override bool hoveredImpl(Rectangle, Vector2) const {
131 
132         return false;
133 
134     }
135 
136 }