1 /// Size locking allows placing restrictions on the size of a node. Using the `SizeLock` node 2 /// template, one can set limits on the maximum size of a node. 3 /// 4 /// Normally, nodes default to using the least space they can: the bare minimum they need 5 /// to display correctly. If their `Node.layout.align` property is set to `fill`, they will 6 /// instead attempt to use all of the space they're given. Using `SizeLock` allows 7 /// for a compromise by placing a limit on how much space a node can use. 8 /// 9 /// Note: 10 /// Using `layout!"fill"` with `SizeLock` will negate the lock's effect. Use a different 11 /// alignment like `"start"`, `"center"` or `"end"`. 12 module fluid.size_lock; 13 14 /// Size limit can be used to center content on a wide screen. By using `sizeLock!node`, 15 /// `.layout!"center"` and `.sizeLimitX`, we can create space around the node for comfortable 16 /// reading. 17 @("SizeLock starter example compiles") 18 unittest { 19 20 import fluid; 21 22 sizeLock!vframe( 23 .sizeLimitX(400), // Maximum width of 800 px 24 .layout!(1, "center"), // Use excess space to center the node 25 label( 26 "By using sizeLock and setting the right limit, a node can be " 27 ~ "forced to use a specific amount of space. This can make your " 28 ~ "app easier to use on a wide screen, without affecting smaller " 29 ~ "windows or displays." 30 ), 31 ); 32 33 } 34 35 import std.algorithm; 36 37 import fluid.node; 38 import fluid.utils; 39 import fluid.style; 40 import fluid.structs; 41 import fluid.backend; 42 43 @safe: 44 45 /// The `sizeLimit` node property sets the maximum amount of space a `SizeLock` node can use. 46 /// `sizeLimit` can only be used with `SizeLock`. 47 /// 48 /// Params: 49 /// x = Maximum width of the node in pixels. 50 /// y = Maximum height of the node in pixels. 51 /// Returns: 52 /// A configured node parameter struct, which can be passed into the `sizeLock` node builder. 53 /// This will be a `SizeBounds` struct if the input parameters are `float` (preferred), 54 /// or `SizeLimit` if they are `size_t` like `uint` or `ulong`. 55 SizeLimit sizeLimit(size_t x, size_t y) { 56 return SizeLimit(x, y); 57 } 58 59 /// ditto 60 SizeLimit sizeLimitX(size_t x) { 61 return SizeLimit(x, 0); 62 } 63 64 /// ditto 65 SizeLimit sizeLimitY(size_t y) { 66 return SizeLimit(0, y); 67 } 68 69 /// ditto 70 FloatSizeLimit sizeLimit(float x, float y) { 71 return FloatSizeLimit( 72 Vector2(x, y) 73 ); 74 } 75 76 /// ditto 77 FloatSizeLimit sizeLimitX(float x) { 78 return FloatSizeLimit( 79 Vector2(x, float.infinity) 80 ); 81 } 82 83 /// ditto 84 FloatSizeLimit sizeLimitY(float y) { 85 return FloatSizeLimit( 86 Vector2(float.infinity, y) 87 ); 88 } 89 90 struct SizeLimit { 91 92 size_t x; 93 size_t y; 94 95 void apply(T)(SizeLock!T node) { 96 node.limit = this; 97 } 98 99 } 100 101 /// This node property defines the maximum size for a `SizeLock` node. Nodes can be given a limit 102 /// by setting either `width` or `height`. 103 /// 104 /// The `init` value defaults to no restrictions. 105 struct FloatSizeLimit { 106 107 import std.math : isFinite; 108 109 /// The imposed limit as a vector. The `x` field is the maximum width, 110 /// and `y` is the maximum height. They both default to `infinity`, effectively not 111 /// setting any limit. 112 auto limit = Vector2(float.infinity, float.infinity); 113 114 /// Returns: 115 /// The maximum width imposed on the node. 116 ref inout(float) width() inout return { 117 return limit.x; 118 } 119 120 /// Returns: 121 /// True if there is a limit applied to node width. 122 bool isWidthLimited() const { 123 return isFinite(width); 124 } 125 126 /// Returns: 127 /// The maximum height imposed on the node. 128 ref inout(float) height() inout return { 129 return limit.y; 130 } 131 132 /// Returns: 133 /// True, if there is a limit applied to node height. 134 bool isHeightLimited() const { 135 return isFinite(height); 136 } 137 138 void apply(T)(SizeLock!T node) { 139 node.sizeLimit = this; 140 } 141 142 } 143 144 /// A node builder that constructs a `SizeLock` node. `sizeLock` can be used with other node 145 /// builders, for example `sizeLock!vframe()` will use a vertical frame as its base, 146 /// while `sizeLock!hframe()` will use a horizontal frame. 147 alias sizeLock(alias T) = simpleConstructor!(SizeLock, T); 148 149 /// `SizeLock` "locks" the size of a node, limiting the amount of space it will use from the space 150 /// it is given. 151 /// 152 /// Size locks are extremely useful for responsible applications, as they can make sure the 153 /// content doesn't span too much space on large screens. For example, a width limit can be 154 /// set with `sizeLimitX`, preventing nodes from spanning the entire width of the screen. 155 class SizeLock(T : Node) : T { 156 157 /// The maximum size of this node. 158 /// If a value on either axis is `0`, limit will not be applied on the axis. 159 /// 160 /// `limit` has been superseded by `sizeLimit`, which uses floats instead of integers, like 161 /// the rest of Fluid. For now, it takes priority over `sizeLimit` if set. 162 SizeLimit limit; 163 164 /// The maximum size of the node. 165 FloatSizeLimit sizeLimit; 166 167 this(T...)(T args) { 168 super(args); 169 } 170 171 override void resizeImpl(Vector2 space) { 172 173 // Virtually limit the available space 174 if (limit.x != 0) space.x = min(space.x, limit.x); 175 else space.x = min(space.x, sizeLimit.width); 176 177 if (limit.y != 0) space.y = min(space.y, limit.y); 178 else space.y = min(space.y, sizeLimit.height); 179 180 // Resize the child 181 super.resizeImpl(space); 182 183 // Apply the limit to the resulting value; fill in remaining space if available 184 if (limit.x != 0) minSize.x = max(space.x, min(limit.x, minSize.x)); 185 else if (sizeLimit.isWidthLimited) { 186 minSize.x = max(space.x, min(sizeLimit.width, minSize.x)); 187 } 188 if (limit.y != 0) minSize.y = max(space.y, min(limit.y, minSize.y)); 189 else if (sizeLimit.isHeightLimited) { 190 minSize.y = max(space.y, min(sizeLimit.height, minSize.y)); 191 } 192 193 } 194 195 } 196 197 /// 198 unittest { 199 200 import fluid; 201 202 // The frame will appear horizontally-centered in the parent node, 203 // and will fill it vertically 204 sizeLock!vframe( 205 .layout!(1, "center", "fill"), 206 .sizeLimitX(400), 207 label("Hello, World!") 208 ); 209 210 }