├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── Test.tscn ├── addons └── FlexContainer │ ├── FlexContainerPlugin.gd │ ├── LICENSE │ ├── README.md │ ├── flex_container.gd │ ├── plugin.cfg │ └── res │ ├── icon.svg │ └── icon.svg.import ├── default_env.tres ├── icon.png ├── icon.png.import ├── project.godot ├── releases └── flex-container-v1.0.0.zip └── screenshots ├── example_1.jpg ├── example_1.jpg.import ├── example_2.jpg └── example_2.jpg.import /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | 4 | # Godot-specific ignores 5 | .import/ 6 | export.cfg 7 | export_presets.cfg 8 | 9 | # Imported translations (automatically generated from CSV files) 10 | *.translation 11 | 12 | # Mono-specific ignores 13 | .mono/ 14 | data_*/ 15 | mono_crash.*.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nif 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlexContainer icon 2 | ### A simple flex container plugin for Godot. 3 | 4 | * Installation 5 | * Usage 6 | * Properties 7 | * Alternatives 8 | * Issues 9 | 10 | 11 | ## Installation 12 | 1. Download the latest version in releases or clone the repository. 13 | 2. Copy the contents of `addons/FlexContainer` into your `res://addons/FlexContainer` directory. 14 | 3. Enable `Dialogue Manager` in your project plugins. 15 | 16 | 17 | ## Usage 18 | 1. Set a grid size using the `Columns` and `Rows` in `Inspector`. 19 | 2. In `Inspector` increase the amount of `Vector2` in `Blocks` property based on the *grid size* (`Columns` × `Rows`). 20 |
This is not required but recommended. It would also be better to avoid going beyond *grid size*. 21 | 3. `Vector2`'s `x` acts as span while `y` acts as row. Customize them depending to your need. 22 | 4. Added children within the node will change size and position corresponding to the `Vector2` in `Blocks`. 23 |
The first `Vector2` corresponds to the first child and so forth. 24 | 25 | **Note: when extending the script, add the `tool` keyword to have the exports show in the `Inspector`.** 26 | 27 | ## Properties 28 | Property | Type | Definition 29 | ---------------- | ---------------- | ------------- 30 | Columns | int | the width or amount of column the *container* will have, forming a *grid*. 31 | Rows | int | the height or amount of rows the *container* will have, forming a *grid*. 32 | Blocks | PoolVector2Array |the placeholder for child nodes within the container. Each `Vector2` represents a block within a *grid*, having its own span (`x`) and row (`y`). It moves from left to right and top to bottom. 33 | Compact | boolean | it fills in the empty spaces within the *container*. 34 | Limit Visible | boolean | it hides children that goes beyond the container's size or outside the size of `Blocks`. 35 | Disable Min Size | boolean | it sets the `rect_min_size` of children to `Vector2.ZERO`. This is currently the default as the container can't handle `rect_min_size` of children. 36 | 37 | 38 | ## Alternatives 39 | * I suggest using DockableContainer for a more cleaner and flexible alternative. It does what FlexContainer does but more! Worth a try. 40 | 41 | 42 | ## Issues 43 | * Currently has no way to properly handle `rect_min_size` of chilren. 44 | * Adding non `Control` type nodes may cause errors or even a crash. Bypass by encapsulating it inside a control type node. 45 | * Resizing isn't fully accurate which may cause some jitter and a pixel or two of misalignment. *It's annoying*. 46 | 47 | **Said issues may be fixed in the future updates. However, if you know a way to fix it, do open up an issue or a pull request. Your contribution would be greatly appreciated.** 48 | 49 | -------------------------------------------------------------------------------- /Test.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://addons/FlexContainer/flex_container.gd" type="Script" id=1] 4 | 5 | [sub_resource type="StyleBoxFlat" id=1] 6 | bg_color = Color( 0, 0, 0, 1 ) 7 | 8 | [node name="Test" type="Control"] 9 | anchor_right = 1.0 10 | anchor_bottom = 1.0 11 | 12 | [node name="PanelContainer" type="PanelContainer" parent="."] 13 | anchor_right = 1.0 14 | anchor_bottom = 1.0 15 | custom_styles/panel = SubResource( 1 ) 16 | 17 | [node name="FlexContainer" type="Container" parent="PanelContainer"] 18 | margin_right = 1024.0 19 | margin_bottom = 600.0 20 | size_flags_horizontal = 3 21 | size_flags_vertical = 3 22 | script = ExtResource( 1 ) 23 | _columns = 3 24 | _rows = 7 25 | _blocks = PoolVector2Array( 1, 3, 2, 2, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ) 26 | _compact = true 27 | _limit_visible = true 28 | _disable_min_size = true 29 | _top_margin = 10 30 | _bottom_margin = 10 31 | _left_margin = 10 32 | _right_margin = 10 33 | _horizontal = 4 34 | _vertical = 4 35 | 36 | [node name="ColorRect" type="ColorRect" parent="PanelContainer/FlexContainer"] 37 | margin_left = 10.0 38 | margin_top = 10.0 39 | margin_right = 342.0 40 | margin_bottom = 255.0 41 | size_flags_horizontal = 3 42 | size_flags_vertical = 3 43 | color = Color( 0.760784, 0.243137, 0.243137, 1 ) 44 | 45 | [node name="ColorRect2" type="ColorRect" parent="PanelContainer/FlexContainer"] 46 | margin_left = 346.0 47 | margin_top = 10.0 48 | margin_right = 1014.0 49 | margin_bottom = 172.0 50 | size_flags_horizontal = 3 51 | size_flags_vertical = 3 52 | color = Color( 0.556863, 0.937255, 0.65098, 1 ) 53 | 54 | [node name="ColorRect3" type="ColorRect" parent="PanelContainer/FlexContainer"] 55 | margin_left = 346.0 56 | margin_top = 176.0 57 | margin_right = 678.0 58 | margin_bottom = 421.0 59 | size_flags_horizontal = 3 60 | size_flags_vertical = 3 61 | color = Color( 0.631373, 0.305882, 0.776471, 1 ) 62 | 63 | [node name="ColorRect4" type="ColorRect" parent="PanelContainer/FlexContainer"] 64 | margin_left = 10.0 65 | margin_top = 425.0 66 | margin_right = 678.0 67 | margin_bottom = 504.0 68 | size_flags_horizontal = 3 69 | size_flags_vertical = 3 70 | color = Color( 0.803922, 0.482353, 0.635294, 1 ) 71 | 72 | [node name="ColorRect5" type="ColorRect" parent="PanelContainer/FlexContainer"] 73 | margin_left = 682.0 74 | margin_top = 176.0 75 | margin_right = 1014.0 76 | margin_bottom = 255.0 77 | size_flags_horizontal = 3 78 | size_flags_vertical = 3 79 | color = Color( 0.968627, 0.913725, 0.45098, 1 ) 80 | 81 | [node name="ColorRect6" type="ColorRect" parent="PanelContainer/FlexContainer"] 82 | margin_left = 10.0 83 | margin_top = 259.0 84 | margin_right = 342.0 85 | margin_bottom = 338.0 86 | size_flags_horizontal = 3 87 | size_flags_vertical = 3 88 | color = Color( 0.372549, 0.752941, 0.854902, 1 ) 89 | 90 | [node name="ColorRect7" type="ColorRect" parent="PanelContainer/FlexContainer"] 91 | margin_left = 682.0 92 | margin_top = 259.0 93 | margin_right = 1014.0 94 | margin_bottom = 338.0 95 | size_flags_horizontal = 3 96 | size_flags_vertical = 3 97 | color = Color( 0.337255, 0.447059, 0.815686, 1 ) 98 | 99 | [node name="ColorRect8" type="ColorRect" parent="PanelContainer/FlexContainer"] 100 | margin_left = 10.0 101 | margin_top = 342.0 102 | margin_right = 342.0 103 | margin_bottom = 421.0 104 | size_flags_horizontal = 3 105 | size_flags_vertical = 3 106 | color = Color( 0.968627, 0.388235, 0.988235, 1 ) 107 | 108 | [node name="ColorRect9" type="ColorRect" parent="PanelContainer/FlexContainer"] 109 | margin_left = 682.0 110 | margin_top = 342.0 111 | margin_right = 1014.0 112 | margin_bottom = 421.0 113 | size_flags_horizontal = 3 114 | size_flags_vertical = 3 115 | color = Color( 0.317647, 0.886275, 0.235294, 1 ) 116 | 117 | [node name="ColorRect10" type="ColorRect" parent="PanelContainer/FlexContainer"] 118 | visible = false 119 | margin_left = 262.0 120 | margin_top = 400.0 121 | margin_right = 510.0 122 | margin_bottom = 591.0 123 | size_flags_horizontal = 3 124 | size_flags_vertical = 3 125 | color = Color( 0.317647, 0.886275, 0.235294, 1 ) 126 | 127 | [node name="Button" type="Button" parent="PanelContainer/FlexContainer"] 128 | visible = false 129 | margin_right = 332.0 130 | margin_bottom = 245.0 131 | -------------------------------------------------------------------------------- /addons/FlexContainer/FlexContainerPlugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorPlugin 3 | 4 | 5 | func _enter_tree(): 6 | add_custom_type("FlexContainer", "Container", preload("flex_container.gd"), preload("res/icon.svg")) 7 | pass 8 | 9 | 10 | func _exit_tree(): 11 | remove_custom_type("FlexContainer") 12 | pass 13 | -------------------------------------------------------------------------------- /addons/FlexContainer/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nif 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/FlexContainer/README.md: -------------------------------------------------------------------------------- 1 | # FlexContainer icon 2 | ### A simple flex container plugin for Godot. 3 | 4 | * Installation 5 | * Usage 6 | * Properties 7 | * Limitations 8 | 9 | 10 | ## Installation 11 | 1. Download the latest version in releases or clone the repository. 12 | 2. Copy the contents of `addons/FlexContainer` into your `res://addons/FlexContainer` directory. 13 | 3. Enable `Dialogue Manager` in your project plugins. 14 | 15 | 16 | ## Usage 17 | 1. Set a grid size using the `Columns` and `Rows` in `Inspector`. 18 | 2. In `Inspector` increase the amount of `Vector2` in `Blocks` property based on the *grid size* (`Columns` × `Rows`). 19 |
This is not required but recommended. It would also be better to avoid going beyond *grid size*. 20 | 3. `Vector2`'s `x` acts as span while `y` acts as row. Customize them depending to your need. 21 | 4. Added children within the node will change size and position corresponding to the `Vector2` in `Blocks`. 22 |
The first `Vector2` corresponds to the first child and so forth. 23 | 24 | **Note: when extending the script, add the `tool` keyword to have the exports show in the `Inspector`.** 25 | 26 | ## Properties 27 | Property | Type | Definition 28 | ---------------- | ---------------- | ------------- 29 | Columns | int | the width or amount of column the *container* will have, forming a *grid*. 30 | Rows | int | the height or amount of rows the *container* will have, forming a *grid*. 31 | Blocks | PoolVector2Array |the placeholder for child nodes within the container. Each `Vector2` represents a block within a *grid*, having its own span (`x`) and row (`y`). It moves from left to right and top to bottom. 32 | Compact | boolean | it fills in the empty spaces within the *container*. 33 | Limit Visible | boolean | it hides children that goes beyond the container's size or outside the size of `Blocks`. 34 | Disable Min Size | boolean | it sets the `rect_min_size` of children to `Vector2.ZERO`. This is currently the default as the container can't handle `rect_min_size` of children. 35 | 36 | 37 | ## Limitations 38 | * Currently has no way to properly handle `rect_min_size` of chilren. 39 | * Adding non `Control` type nodes may cause errors or even a crash. Bypass by encapsulating it inside a control type node. 40 | * Resizing isn't fully accurate which causes jitter and a pixel or two of misalignment. *It's annoying*. 41 | 42 | **Said issues may be fixed in the future updates. However, if you know a way to fix it, do open up an issue or a pull request. Your contribution would be greatly apprciated** 43 | 44 | -------------------------------------------------------------------------------- /addons/FlexContainer/flex_container.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Container 3 | 4 | #TODO: 5 | #>Add a fix/implementation for resizing with rect_min_size of children. 6 | 7 | #Size (of the grid) 8 | var _columns := 1 setget set_columns, get_columns 9 | var _rows := 1 setget set_rows, get_rows 10 | 11 | #Blocks (the placeholder for the span and row of the container's children) 12 | var _blocks := PoolVector2Array([Vector2(1,1)]) setget set_blocks, get_blocks 13 | 14 | #Content Margin 15 | var _top_margin := 0 setget set_top_margin, get_top_margin 16 | var _bottom_margin := 0 setget set_bottom_margin, get_bottom_margin 17 | var _right_margin := 0 setget set_right_margin, get_right_margin 18 | var _left_margin := 0 setget set_left_margin, get_left_margin 19 | 20 | #Separation (or space between Blocks) 21 | var _horizontal := 4 setget set_horizontal, get_horizontal 22 | var _vertical := 4 setget set_vertical, get_vertical 23 | 24 | #Flags 25 | var _compact := false setget set_compact, get_compact #Fill in empty spaces with the next block that fits 26 | var _limit_visible := false setget set_limit_visible, get_limit_visible #Hide children outside of Blocks' array size. 27 | var _disable_min_size := true setget set_disable_min_size, get_disable_min_size 28 | #^Proper resize with minimum has not been implemented. Turning _disable_min_size off can cause unexpected crashes or lag. 29 | #^It is only applicable to use if all children has proper rect_min_size values. 30 | var _child_count := 0 #Refer to _notification method 31 | 32 | #Creates an export for the FlexContainer's inspector. 33 | func _get_property_list() -> Array: 34 | return [ 35 | { 36 | "name": "FlexContainer", 37 | "type": TYPE_NIL, 38 | "usage": PROPERTY_USAGE_CATEGORY, 39 | }, 40 | { 41 | "name": "_columns", 42 | "type": TYPE_INT, 43 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, 44 | }, 45 | { 46 | "name": "_rows", 47 | "type": TYPE_INT, 48 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 49 | }, 50 | { 51 | "name": "_blocks", 52 | "type": TYPE_VECTOR2_ARRAY, 53 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 54 | }, 55 | { 56 | "name": "_compact", 57 | "type": TYPE_BOOL, 58 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 59 | }, 60 | { 61 | "name": "_limit_visible", 62 | "type": TYPE_BOOL, 63 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 64 | }, 65 | { 66 | "name": "_disable_min_size", 67 | "type": TYPE_BOOL, 68 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 69 | }, 70 | { 71 | "name": "Content Margin", 72 | "type": TYPE_NIL, 73 | "usage": PROPERTY_USAGE_GROUP, 74 | }, 75 | { 76 | "name": "_top_margin", 77 | "type": TYPE_INT, 78 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 79 | }, 80 | { 81 | "name": "_bottom_margin", 82 | "type": TYPE_INT, 83 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 84 | }, 85 | { 86 | "name": "_left_margin", 87 | "type": TYPE_INT, 88 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 89 | }, 90 | { 91 | "name": "_right_margin", 92 | "type": TYPE_INT, 93 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 94 | }, 95 | { 96 | "name": "Separation", 97 | "type": TYPE_NIL, 98 | "usage": PROPERTY_USAGE_GROUP, 99 | }, 100 | { 101 | "name": "_horizontal", 102 | "type": TYPE_INT, 103 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 104 | }, 105 | { 106 | "name": "_vertical", 107 | "type": TYPE_INT, 108 | "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE 109 | }, 110 | ] 111 | 112 | #Gives the option to revert a property to default state. 113 | func property_can_revert(property): 114 | match property: 115 | "_columns": 116 | return true 117 | "_rows": 118 | return true 119 | "_blocks": 120 | return true 121 | "_top_margin": 122 | return true 123 | "_bottom_margin": 124 | return true 125 | "_left_margin": 126 | return true 127 | "_right_margin": 128 | return true 129 | "_horizontal": 130 | return true 131 | "_vertical": 132 | return true 133 | "_compact": 134 | return true 135 | "_limit_visible": 136 | return true 137 | "_disable_min_size": 138 | return true 139 | _: 140 | return false 141 | 142 | #Sets the default value on revert. 143 | func property_get_revert(property): 144 | match property: 145 | "_columns": 146 | return 1 147 | "_rows": 148 | return 1 149 | "_blocks": 150 | return PoolVector2Array([Vector2(1,1)]) 151 | "_top_margin": 152 | return 0 153 | "_bottom_margin": 154 | return 0 155 | "_left_margin": 156 | return 0 157 | "_right_margin": 158 | return 0 159 | "_horizontal": 160 | return 4 161 | "_vertical": 162 | return 4 163 | "_compact": 164 | return false 165 | "_limit_visible": 166 | return false 167 | "_disable_min_size": 168 | return true 169 | 170 | 171 | #Setget _columns 172 | func set_columns(value): 173 | if value > 0: 174 | _columns = value 175 | _sort_display() 176 | return 177 | _columns = 1 178 | _sort_display() 179 | 180 | func get_columns() -> int: 181 | return _columns 182 | 183 | 184 | #Setget _rows 185 | func set_rows(value): 186 | if value > 0: 187 | _rows = value 188 | _sort_display() 189 | return 190 | _rows = 1 191 | _sort_display() 192 | 193 | func get_rows() -> int: 194 | return _rows 195 | 196 | 197 | #Setget Blocks 198 | #Note: 199 | #For-loop changes doesn't affect the array in any way. Thus a count solution. 200 | #Vector values in array also does not visually update instantly, sadly. 201 | func set_blocks(value): 202 | if value.size() > 0: 203 | var new_blocks = value 204 | var count = new_blocks.size() 205 | for vector in new_blocks: 206 | if new_blocks[count-1].x < 1: 207 | new_blocks[count-1].x = 1 208 | if new_blocks[count-1].y < 1: 209 | new_blocks[count-1].y = 1 210 | count-=1 211 | _blocks = new_blocks 212 | _sort_display() 213 | return 214 | _blocks = PoolVector2Array([Vector2(1,1)]) 215 | _sort_display() 216 | 217 | func get_blocks() -> PoolVector2Array: 218 | return _blocks 219 | 220 | 221 | #Setget Top Margin 222 | func set_top_margin(value): 223 | if value > 0: 224 | _top_margin = value 225 | _sort_display() 226 | return 227 | _top_margin = 0 228 | _sort_display() 229 | 230 | func get_top_margin() -> int: 231 | return _top_margin 232 | 233 | 234 | #Setget Bottom Margin 235 | func set_bottom_margin(value): 236 | if value > 0: 237 | _bottom_margin = value 238 | _sort_display() 239 | return 240 | _bottom_margin = 0 241 | _sort_display() 242 | 243 | func get_bottom_margin() -> int: 244 | return _bottom_margin 245 | 246 | 247 | #Setget Left Margin 248 | func set_left_margin(value): 249 | if value > 0: 250 | _left_margin = value 251 | _sort_display() 252 | return 253 | _left_margin = 0 254 | _sort_display() 255 | 256 | func get_left_margin() -> int: 257 | return _left_margin 258 | 259 | 260 | #Setget Right Margin 261 | func set_right_margin(value): 262 | if value > 0: 263 | _right_margin = value 264 | _sort_display() 265 | return 266 | _right_margin = 0 267 | _sort_display() 268 | 269 | func get_right_margin() -> int: 270 | return _right_margin 271 | 272 | 273 | #Setget Horizontal 274 | func set_horizontal(value): 275 | if value > 0: 276 | _horizontal = value 277 | _sort_display() 278 | return 279 | _horizontal = 4 280 | _sort_display() 281 | 282 | func get_horizontal() -> int: 283 | return _horizontal 284 | 285 | 286 | #Setget Vertical 287 | func set_vertical(value): 288 | if value > 0: 289 | _vertical = value 290 | _sort_display() 291 | return 292 | _vertical = 4 293 | _sort_display() 294 | 295 | func get_vertical() -> int: 296 | return _vertical 297 | 298 | 299 | #Setget Compact 300 | func set_compact(value): 301 | _compact = value 302 | _sort_display() 303 | 304 | func get_compact() -> bool: 305 | return _compact 306 | 307 | 308 | #Setget Limit Visible 309 | func set_limit_visible(value): 310 | _limit_visible = value 311 | if _limit_visible: 312 | _hide_unused(get_children()) 313 | else: 314 | _show_unused(get_children()) 315 | 316 | func get_limit_visible() -> bool: 317 | return _limit_visible 318 | 319 | 320 | #Setget Disabled Min Size 321 | func set_disable_min_size(value): 322 | _disable_min_size = value 323 | if _disable_min_size: 324 | _remove_min_rect(get_children()) 325 | 326 | func get_disable_min_size() -> bool: 327 | return _disable_min_size 328 | 329 | 330 | # Called when the node enters the scene tree for the first time. 331 | func _ready(): 332 | connect("item_rect_changed", self, "_rect_resized") 333 | connect("visibility_changed", self, "_visibility_changed") 334 | 335 | 336 | func _rect_resized(): 337 | _sort_display() 338 | 339 | 340 | func _visibility_changed(): 341 | _sort_display() 342 | 343 | 344 | func _notification(what): 345 | if Engine.editor_hint: 346 | #The condition below allows checking for any node enter/exit and reposition. 347 | if _child_count != get_child_count() || what == NOTIFICATION_DRAW: 348 | _child_count = get_child_count() 349 | _sort_display() 350 | 351 | 352 | func _sort_display(): 353 | var content_size := Vector2((rect_size.x - (_left_margin + _right_margin)), (rect_size.y - (_top_margin + _bottom_margin))) 354 | if _blocks.size() > 1: 355 | if _columns > 1: 356 | content_size.x -= (_columns-1) * _horizontal 357 | if _rows > 1: 358 | content_size.y -= (_rows-1) * _vertical 359 | var children := get_children() 360 | for index in range(0, get_child_count(), 1): 361 | if index+1 <= _blocks.size() and index+1 <= (_columns*_rows): 362 | if not children[index].visible and not children[index] is Popup: 363 | children[index].show() 364 | _resize(index, children, content_size) 365 | if _compact: 366 | _reposition_compact(index, children, content_size) 367 | continue #Goes to next child 368 | _reposition(index, children, content_size) 369 | if _disable_min_size: 370 | _remove_min_rect(children) 371 | if _limit_visible: 372 | _hide_unused(children) 373 | return #Exits 374 | _show_unused(children) 375 | 376 | 377 | func _hide_unused(children): 378 | for i in range(get_child_count()-1, -1, -1): 379 | if children[i].visible == true: 380 | if i+1 > _blocks.size() or i+1 > (_columns*_rows): 381 | children[i].hide() 382 | elif children[i].get_rect().end.y >= rect_size.y: 383 | children[i].hide() 384 | 385 | 386 | func _show_unused(children): 387 | for i in range(get_child_count()-1, -1, -1): 388 | if children[i].visible == false: 389 | if i+1 > _blocks.size() or i+1 > (_columns*_rows): 390 | children[i].show() 391 | elif children[i].get_rect().end.y > rect_size.y: 392 | children[i].show() 393 | 394 | 395 | func _remove_min_rect(children): 396 | for i in range(0, get_child_count(), 1): 397 | if children[i].rect_min_size > Vector2.ZERO: 398 | children[i].rect_min_size = Vector2.ZERO 399 | 400 | 401 | #Note: 402 | #>ceil lessens the jitter but not fully, haven't found a solution. 403 | func _resize(index, children, content_size): 404 | var block = _blocks[index] 405 | if block.x > _columns: 406 | block.x = _columns 407 | if block.y > _rows: 408 | block.y = _rows 409 | var width = (round(content_size.x / _columns) * block.x) + (_horizontal * (block.x-1)) 410 | var height = (round(content_size.y / _rows) * block.y) + (_vertical * (block.y-1)) 411 | var size := Vector2(width, height) 412 | children[index].rect_size = size 413 | 414 | 415 | #Note: 416 | #>ceil lessens the jitter but not fully, haven't found a solution. 417 | func _reposition(index, children, content_size): 418 | if index > 0: 419 | children[index].rect_position.x = children[index-1].get_rect().end.x + _horizontal 420 | children[index].rect_position.y = children[index-1].rect_position.y 421 | while true: 422 | var flag = 0 423 | 424 | #IF child is outside of (self) parent's rect_size. 425 | if children[index].get_rect().end.x > rect_size.x+(children[index].get_rect().size.x/2)-_right_margin: 426 | children[index].rect_position.y += (round(content_size.y / _rows) + _vertical) 427 | children[index].rect_position.x = _left_margin 428 | flag+=1 429 | 430 | #IF child is intersecting with another child loaded before it. 431 | var intersecting_rect : Rect2 = _get_intersecting_rect(index, children, true) 432 | if !intersecting_rect.has_no_area(): #Double negative 433 | children[index].rect_position.x = intersecting_rect.end.x + _horizontal 434 | flag+=1 435 | 436 | if flag == 0: 437 | break 438 | else: #IF initial 439 | children[index].rect_position = Vector2(_left_margin, _top_margin) 440 | 441 | 442 | func _get_intersecting_rect(index, children, reverse:bool=false) -> Rect2: 443 | var start := 0 444 | var end = index 445 | var step := 1 446 | if reverse: 447 | start = index 448 | end = 0 449 | step = -1 450 | for i in range(start, end, step): 451 | var x = i 452 | if reverse: 453 | x-=1 454 | if children[index].get_rect().intersects(children[x].get_rect()): 455 | return children[i-1].get_rect() 456 | return Rect2() #Empty, because for some reason you can't use null :harold: 457 | 458 | 459 | func _reposition_compact(index, children, content_size): 460 | var block_rect_size := Vector2(round(content_size.x / _columns), round(content_size.y / _rows)) 461 | children[index].rect_position = Vector2(_left_margin, _top_margin) 462 | if index > 0: 463 | for i in range(0, (_columns*_rows), 1): 464 | var flag = 0 465 | 466 | if children[index].get_rect().end.x > rect_size.x+(children[index].get_rect().size.x/2)-_right_margin: 467 | flag+=1 468 | if !_get_intersecting_rect(index, children).has_no_area(): 469 | flag+=1 470 | 471 | if flag == 0: 472 | children[index].rect_position 473 | break 474 | 475 | children[index].rect_position.x += block_rect_size.x + _horizontal 476 | if (i+1)%_columns == 0: 477 | children[index].rect_position.y += block_rect_size.y + _vertical 478 | children[index].rect_position.x = _left_margin 479 | -------------------------------------------------------------------------------- /addons/FlexContainer/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="FlexContainer" 4 | description="A simple flex container that can handle different column and row ratio." 5 | author="Nif" 6 | version="1.0.0" 7 | script="FlexContainerPlugin.gd" 8 | -------------------------------------------------------------------------------- /addons/FlexContainer/res/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 48 | -------------------------------------------------------------------------------- /addons/FlexContainer/res/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.svg-63bdbe80b13834dfdcf2cf219f9eddee.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/FlexContainer/res/icon.svg" 13 | dest_files=[ "res://.import/icon.svg-63bdbe80b13834dfdcf2cf219f9eddee.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nif-kun/godot-flex-container/a7b6e9dc69e45d80e0da7f826c315cc7cd06398f/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | [application] 12 | 13 | config/name="FlexContainer" 14 | run/main_scene="res://Test.tscn" 15 | config/icon="res://icon.png" 16 | 17 | [editor_plugins] 18 | 19 | enabled=PoolStringArray( "res://addons/FlexContainer/plugin.cfg" ) 20 | 21 | [gui] 22 | 23 | common/drop_mouse_on_gui_input_disabled=true 24 | 25 | [mono] 26 | 27 | project/assembly_name="FlexContainer" 28 | 29 | [physics] 30 | 31 | common/enable_pause_aware_picking=true 32 | 33 | [rendering] 34 | 35 | environment/default_environment="res://default_env.tres" 36 | -------------------------------------------------------------------------------- /releases/flex-container-v1.0.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nif-kun/godot-flex-container/a7b6e9dc69e45d80e0da7f826c315cc7cd06398f/releases/flex-container-v1.0.0.zip -------------------------------------------------------------------------------- /screenshots/example_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nif-kun/godot-flex-container/a7b6e9dc69e45d80e0da7f826c315cc7cd06398f/screenshots/example_1.jpg -------------------------------------------------------------------------------- /screenshots/example_1.jpg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/example_1.jpg-59bdeb6675c6331eb9123b20d2fc8f1b.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://screenshots/example_1.jpg" 13 | dest_files=[ "res://.import/example_1.jpg-59bdeb6675c6331eb9123b20d2fc8f1b.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /screenshots/example_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nif-kun/godot-flex-container/a7b6e9dc69e45d80e0da7f826c315cc7cd06398f/screenshots/example_2.jpg -------------------------------------------------------------------------------- /screenshots/example_2.jpg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/example_2.jpg-ffcc5f90929c7b15328b4b136df41f39.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://screenshots/example_2.jpg" 13 | dest_files=[ "res://.import/example_2.jpg-ffcc5f90929c7b15328b4b136df41f39.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | --------------------------------------------------------------------------------