inv ui
201
godot/LICENSE
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatableControl.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="45.254834"
|
||||
inkscape:cx="6.6843688"
|
||||
inkscape:cy="8.0322911"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect92"
|
||||
style="fill:#8eef97;stroke-width:1.77191;stroke-linecap:round;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="M 8.0820312 1.5 L 8.0820312 8.0605469 L 14.642578 8.0605469 L 14.642578 1.5 L 8.0820312 1.5 z M 7.6875 1.8945312 L 1.3574219 14.5 L 14.25 8.4550781 L 7.6875 8.4550781 L 7.6875 1.8945312 z " />
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b61f2s0sdeivn"
|
||||
path="res://.godot/imported/AnimatableControl.svg-ea6d8910d2b3058bfac46c0758163c04.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/AnimatableControl.svg"
|
||||
dest_files=["res://.godot/imported/AnimatableControl.svg-ea6d8910d2b3058bfac46c0758163c04.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatableMount.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="6.703125"
|
||||
inkscape:cy="8.265625"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="path76"
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:square;stroke-miterlimit:3.9;stroke-dasharray:18.0028, 18.0028;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 0 0 L 0 10.75 L 1.5 10.75 L 1.5 1.5 L 10.251953 1.5 L 10.251953 0 L 0 0 z M 8 3 L 8 8 L 13 8 L 13 3 L 8 3 z M 7.6992188 3.3007812 L 2.875 12.90625 L 12.699219 8.3007812 L 7.6992188 8.3007812 L 7.6992188 3.3007812 z M 14.5 5.25 L 14.5 14.5 L 5.7460938 14.5 L 5.7460938 16 L 16 16 L 16 5.25 L 14.5 5.25 z " />
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://td5qpky5w1jp"
|
||||
path="res://.godot/imported/AnimatableMount.svg-c55e9d75134e187a953286234a72f1d8.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/AnimatableMount.svg"
|
||||
dest_files=["res://.godot/imported/AnimatableMount.svg-c55e9d75134e187a953286234a72f1d8.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatableScrollControl.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="12.578125"
|
||||
inkscape:cy="5.328125"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect92"
|
||||
style="fill:#8eef97;stroke-width:1.29486;stroke-linecap:round;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="M 5.09375 1 L 3.09375 3 L 7.09375 3 L 5.09375 1 z M 7.09375 3 L 7.09375 7 L 9.09375 5 L 7.09375 3 z M 7.09375 7 L 3.09375 7 L 5.09375 9 L 7.09375 7 z M 3.09375 7 L 3.09375 3 L 1.09375 5 L 3.09375 7 z M 9.8476562 5 L 9.8476562 9.7949219 L 14.642578 9.7949219 L 14.642578 5 L 9.8476562 5 z M 9.5605469 5.2871094 L 4.9335938 14.5 L 14.355469 10.082031 L 9.5605469 10.082031 L 9.5605469 5.2871094 z " />
|
||||
</svg>
|
After Width: | Height: | Size: 2 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d0wty6o4l0n16"
|
||||
path="res://.godot/imported/AnimatableScrollControl.svg-82ff252057e45e07beda388858928afc.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/AnimatableScrollControl.svg"
|
||||
dest_files=["res://.godot/imported/AnimatableScrollControl.svg-82ff252057e45e07beda388858928afc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatableTransformationMount.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="7.828125"
|
||||
inkscape:cy="10.046875"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="path76"
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:square;stroke-miterlimit:3.9;stroke-dasharray:18.0028, 18.0028;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 0 0 L 0 3 L 1.5 3 L 1.5 1.5 L 3 1.5 L 3 0 L 0 0 z M 3 1.5 L 3 2.5 L 8 2.5 L 8 1.5 L 3 1.5 z M 8 1.5 L 10.251953 1.5 L 10.251953 0 L 8 0 L 8 1.5 z M 1.5 3 L 1.5 8 L 2.5 8 L 2.5 3 L 1.5 3 z M 1.5 8 L 0 8 L 0 10.75 L 1.5 10.75 L 1.5 8 z M 8 3 L 8 8 L 13 8 L 13 3 L 8 3 z M 7.6992188 3.3007812 L 2.875 12.90625 L 12.699219 8.3007812 L 7.6992188 8.3007812 L 7.6992188 3.3007812 z M 14.5 5.25 L 14.5 8 L 16 8 L 16 5.25 L 14.5 5.25 z M 14.5 8 L 13.5 8 L 13.5 13 L 14.5 13 L 14.5 8 z M 14.5 13 L 14.5 14.5 L 13 14.5 L 13 16 L 16 16 L 16 13 L 14.5 13 z M 13 14.5 L 13 13.5 L 8 13.5 L 8 14.5 L 13 14.5 z M 8 14.5 L 5.7460938 14.5 L 5.7460938 16 L 8 16 L 8 14.5 z " />
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c05srll0xjnus"
|
||||
path="res://.godot/imported/AnimatableTransformationMount.svg-3384cfe8cf7ff650173fe5f295bf32bd.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/AnimatableTransformationMount.svg"
|
||||
dest_files=["res://.godot/imported/AnimatableTransformationMount.svg-3384cfe8cf7ff650173fe5f295bf32bd.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatableVisibleControl.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="12.578125"
|
||||
inkscape:cy="5.515625"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect92"
|
||||
style="fill:#8eef97;stroke-width:1.29486;stroke-linecap:round;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="M 4.7441406 0.83203125 C 3.1236476 0.83203125 1.075573 2.0662754 0.33789062 4.4472656 A 0.63374836 0.63374836 0 0 0 0.33789062 4.796875 C 1.0464205 7.3039809 3.1597713 8.4375 4.7441406 8.4375 C 6.3285098 8.4375 8.4432838 7.3030246 9.15625 4.8085938 A 0.63374836 0.63374836 0 0 0 9.15625 4.4609375 C 8.4591276 2.0590336 6.3627323 0.83203125 4.7441406 0.83203125 z M 4.7441406 2.0996094 A 2.5351562 2.5351562 0 0 1 4.7441406 7.1699219 A 2.5351562 2.5351562 0 0 1 4.7441406 2.0996094 z M 4.7441406 3.3671875 A 1.2675781 1.2675781 0 0 0 4.7441406 5.9023438 A 1.2675781 1.2675781 0 0 0 4.7441406 3.3671875 z M 9.8476562 5 L 9.8476562 9.7949219 L 14.642578 9.7949219 L 14.642578 5 L 9.8476562 5 z M 9.5605469 5.2871094 L 4.9335938 14.5 L 14.355469 10.082031 L 9.5605469 10.082031 L 9.5605469 5.2871094 z " />
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://3bug5vfuq1vw"
|
||||
path="res://.godot/imported/AnimatableVisibleControl.svg-231049b2351dc86fdfb539faef83c988.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/AnimatableVisibleControl.svg"
|
||||
dest_files=["res://.godot/imported/AnimatableVisibleControl.svg-231049b2351dc86fdfb539faef83c988.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatablePercentControl.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="5.484375"
|
||||
inkscape:cy="9.140625"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect92-2"
|
||||
style="fill:#8eef97;stroke-width:1.29486;stroke-linecap:round;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="M 6.1191406 1.578125 L 2.6601562 7.5664062 L 3.2597656 7.9121094 L 6.7167969 1.9238281 L 6.1191406 1.578125 z M 2.3964844 1.9824219 A 1.3831828 1.3831828 0 0 0 1.0957031 2.6699219 A 1.3831828 1.3831828 0 0 0 1.6015625 4.5605469 A 1.3831828 1.3831828 0 0 0 3.4902344 4.0527344 A 1.3831828 1.3831828 0 0 0 2.984375 2.1640625 A 1.3831828 1.3831828 0 0 0 2.3964844 1.9824219 z M 2.2382812 2.8457031 A 0.51869356 0.51869356 0 0 1 2.5527344 2.9121094 A 0.51869356 0.51869356 0 0 1 2.7421875 3.6210938 A 0.51869356 0.51869356 0 0 1 2.0332031 3.8105469 A 0.51869356 0.51869356 0 0 1 1.84375 3.1035156 A 0.51869356 0.51869356 0 0 1 2.2382812 2.8457031 z M 7.1875 4.75 A 1.3831828 1.3831828 0 0 0 5.8867188 5.4375 A 1.3831828 1.3831828 0 0 0 6.3925781 7.3261719 A 1.3831828 1.3831828 0 0 0 8.2832031 6.8203125 A 1.3831828 1.3831828 0 0 0 7.7753906 4.9296875 A 1.3831828 1.3831828 0 0 0 7.1875 4.75 z M 9.8476562 5 L 9.8476562 9.7949219 L 14.642578 9.7949219 L 14.642578 5 L 9.8476562 5 z M 9.5605469 5.2871094 L 4.9335938 14.5 L 14.355469 10.082031 L 9.5605469 10.082031 L 9.5605469 5.2871094 z M 7.03125 5.6132812 A 0.51869356 0.51869356 0 0 1 7.34375 5.6796875 A 0.51869356 0.51869356 0 0 1 7.5332031 6.3886719 A 0.51869356 0.51869356 0 0 1 6.8261719 6.578125 A 0.51869356 0.51869356 0 0 1 6.6347656 5.8691406 A 0.51869356 0.51869356 0 0 1 7.03125 5.6132812 z " />
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cice0m617g5ks"
|
||||
path="res://.godot/imported/AnimatableZoneControl.svg-52fb2c5380b72522d734b8b9ba5fc752.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/AnimatableZoneControl.svg"
|
||||
dest_files=["res://.godot/imported/AnimatableZoneControl.svg-52fb2c5380b72522d734b8b9ba5fc752.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatedSwitch.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="3.421875"
|
||||
inkscape:cy="6.890625"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g2" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)">
|
||||
<path
|
||||
id="path1"
|
||||
style="fill:#8eef97;fill-opacity:1;stroke-width:16.2328"
|
||||
d="M 39.682511,-18.797691 V 9.4829989 H -45.190974 V 37.795393 H 39.682511 V 66.076082 L 82.135106,23.623343 Z m -69.908866,93.370662 a 64.931228,64.931449 0 0 0 -64.931228,64.931449 64.931228,64.931449 0 0 0 64.931228,64.93145 H 67.170487 A 64.931228,64.931449 0 0 0 132.10172,139.50442 64.931228,64.931449 0 0 0 67.170487,74.572971 Z m 0,32.465719 h 41.247816 a 64.931228,64.931449 0 0 0 -8.7822021,32.46573 64.931228,64.931449 0 0 0 8.7822021,32.46572 h -41.247816 a 32.465614,32.465724 0 0 1 -32.465614,-32.46572 32.465614,32.465724 0 0 1 32.465614,-32.46573 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b40v8yi30eall"
|
||||
path="res://.godot/imported/AnimatedSwitch.svg-76f991124fb01f03d12bfe5e3a3c31ba.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/AnimatedSwitch.svg"
|
||||
dest_files=["res://.godot/imported/AnimatedSwitch.svg-76f991124fb01f03d12bfe5e3a3c31ba.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="BoundsCheck.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="45.254834"
|
||||
inkscape:cx="5.8225824"
|
||||
inkscape:cy="8.4300387"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<path
|
||||
id="path2-5"
|
||||
style="fill:#8eef97;fill-opacity:1;stroke-width:0.0595858;stroke-dasharray:none"
|
||||
d="m 8.709677,2.538 4.663522,3.678581 c 0.06308,0.04997 0.10036,0.09344 0.117877,0.132606 0.01032,0.02266 0.01132,0.04335 0.0049,0.05894 -0.01003,0.02543 -0.03511,0.04883 -0.07122,0.07367 0,0 -0.181159,0.08803 -0.306971,0.105593 -0.0024,-1.62e-4 -0.0056,-4.87e-4 -0.0074,0 l -1.458736,0.201365 c -0.02172,0.003 -0.04117,0.0091 -0.05894,0.01965 -0.03464,0.02055 -0.05989,0.05477 -0.07122,0.09331 -0.0061,0.02076 -0.0081,0.04104 -0.0049,0.06385 0.0024,0.01737 0.0074,0.03438 0.01474,0.04911 h -0.0025 l 0.86689,1.775442 c 0.002,0.0049 -1.63e-4,0.01209 -0.0025,0.01965 -0.0036,0.0093 -0.0085,0.01921 -0.01719,0.02455 l -0.928285,0.454296 h -0.0025 c -0.0027,8.85e-4 -0.0049,0.0036 -0.0074,0.0049 -0.0084,0.0024 -0.01831,5.69e-4 -0.02701,-0.0025 -0.0075,-0.0023 -0.01151,-0.0049 -0.01474,-0.0098 L 10.522049,7.510741 c -0.0096,-0.01979 -0.02367,-0.03804 -0.03929,-0.05157 -0.01563,-0.01353 -0.03228,-0.02293 -0.05158,-0.02947 -0.01929,-0.0065 -0.04077,-0.01101 -0.06139,-0.0098 -0.02063,0.0012 -0.04175,0.0075 -0.06139,0.01719 -0.01537,0.0079 -0.02968,0.01783 -0.04175,0.02947 L 9.210663,8.500392 c -0.04546,0.04468 -0.0919,0.08214 -0.137524,0.112961 -0.04568,0.03035 -0.09044,0.05501 -0.132612,0.06876 -0.04125,0.01322 -0.07696,0.01753 -0.103142,0.0098 -0.0082,-0.0023 -0.01483,-0.0082 -0.02211,-0.01473 -0.0073,-0.0065 -0.01385,-0.01378 -0.01965,-0.02456 -0.01002,-0.01848 -0.01852,-0.04172 -0.02455,-0.07121 -0.006,-0.02949 -0.0096,-0.06523 -0.0098,-0.105594 z m 2.584862,7.088728 h -0.0018 c -0.0026,8.69e-4 -0.0069,0.0052 -0.0069,0.0052 0,0 -0.0179,0.0011 -0.02599,-0.0017 -0.007,-0.0021 -0.01086,-0.0058 -0.01387,-0.0104 L 10.433431,7.973832 c -0.009,-0.01841 -0.02185,-0.03592 -0.03638,-0.04851 -0.01454,-0.01259 -0.03057,-0.02164 -0.04852,-0.02772 -0.01795,-0.0061 -0.03798,-0.0098 -0.05717,-0.0087 -0.01919,0.0011 -0.0389,0.0066 -0.05717,0.01559 -0.01431,0.0073 -0.02689,0.01689 -0.03811,0.02772 L 8.85868,9.248366 c -0.0423,0.04157 -0.08577,0.07701 -0.128214,0.105685 -0.04249,0.02823 -0.08378,0.05131 -0.123014,0.06411 -0.03838,0.0123 -0.07267,0.01583 -0.09703,0.0087 -0.0075,-0.0021 -0.01229,-0.0078 -0.01906,-0.01386 -0.0067,-0.006 -0.01367,-0.0125 -0.01905,-0.02252 -0.0093,-0.01719 -0.01692,-0.04013 -0.02252,-0.06757 -0.0056,-0.02744 -0.0084,-0.05947 -0.0087,-0.09702 -0.02008,-1.96641 -0.02692,-3.9331044 -0.05891,-5.8992921 0,0 -0.06482,-0.00247 -0.08144,-0.00173 -0.01662,7.312e-4 -0.03366,0.00254 -0.05025,0.00693 -0.01678,0.00327 -0.03348,0.00903 -0.04851,0.019058 -0.02004,0.012481 -0.03529,0.029237 -0.05025,0.046778 -0.0072,0.00277 -0.01483,0.010827 -0.02945,0.04158 C 6.7916131,6.238333 2.5,13.462 2.5,13.462 l 8.895029,-3.556333 c 0.0067,-0.0023 0.01335,-0.004 0.01906,-0.0069 l 0.589087,-0.28933 0.28588,-0.140337 c 0.0034,-0.0014 0.0075,-0.0014 0.01039,-0.0035 0.07571,-0.04178 0.129792,-0.112781 0.155937,-0.188847 0.02684,-0.07823 -0.01733,-0.206172 -0.01733,-0.206172 0,0 -0.762799,0.369992 -1.143522,0.556148 z M 2.2558594,0.77539062 C 1.8121817,0.77222361 1.5771259,0.83320546 1.2539062,0.91210938 l -0.056641,0.0351562 -0.078125,0.0585938 -0.072266,0.066406 -0.0644531,0.072266 -0.0585938,0.078125 -0.0507812,0.083984 -0.0429688,0.089844 -0.0351562,0.09375 L 0.7695305,1.5878904 0.7539055,1.6894529 0.75,1.7792969 V 4.6835938 5.1972656 H 1.7773438 V 4.6835938 1.8046875 1.8027344 H 5.875 6.3886719 V 0.77539062 H 5.875 Z m 5.1601562,0 V 1.8027344 h 0.5136719 4.1074215 0.513672 V 0.77539062 H 12.037109 7.9296875 Z m 6.1621094,0 V 1.8027344 h 0.511719 0.130859 0.002 v 0.00195 3.1621094 0.5136719 H 15.25 v -0.5136719 -3.1875 L 15.2461,1.6894498 15.23047,1.5878873 15.20508,1.4902313 15.16992,1.3964813 15.12695,1.3066373 15.07617,1.2226533 15.01758,1.1445283 14.95313,1.0722623 14.88086,1.0058563 14.80273,0.9472625 14.71875,0.8964813 14.62891,0.8535125 14.53516,0.8203094 14.4375,0.7949188 14.335938,0.7792938 14.246098,0.7753878 h -0.15625 z M 0.75,6.2246094 v 0.5136718 4.1074218 0.513672 H 1.7773438 V 10.845703 6.7382812 6.2246094 Z m 13.472656,0.2832031 v 0.5136719 4.1074216 0.513672 H 15.25 V 11.128906 7.0214844 6.5078125 Z M 0.75,12.386719 v 0.511719 1.347656 l 0.003906,0.08984 0.015625,0.101562 0.0253906,0.09766 0.0351562,0.09375 0.0429688,0.08984 0.0507812,0.08398 0.0585938,0.07813 0.0644531,0.07227 0.072266,0.06445 0.078125,0.05859 0.083984,0.05078 0.089844,0.04297 0.09375,0.03516 0.097656,0.02539 0.1015625,0.01563 0.089844,0.0039 H 3.75 4.2636719 v -1.02734 H 3.75 1.7792969 1.7773438 v -0.002 -1.322265 -0.511719 z m 13.472656,0.283203 v 0.513672 1.037109 0.002 h -0.002 -2.253906 -0.513672 V 15.25 h 0.513672 2.279297 l 0.08984,-0.0039 0.101562,-0.01563 0.09766,-0.02539 0.09375,-0.03516 0.08984,-0.04297 0.08398,-0.05078 0.07813,-0.05859 0.07227,-0.06445 0.06445,-0.07227 0.05859,-0.07813 0.05078,-0.08398 0.04297,-0.08984 0.03516,-0.09375 0.02539,-0.09766 0.01563,-0.101562 0.0039,-0.08984 v -1.0625 -0.513672 z M 5.2910156,14.222656 V 15.25 H 5.8046875 9.9121094 10.425781 V 14.222656 H 9.9121094 5.8046875 Z" />
|
||||
</svg>
|
After Width: | Height: | Size: 6.5 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bladvsiu7rha1"
|
||||
path="res://.godot/imported/BoundsCheck.svg-02b170ef6b912468a1f094b439993793.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/BoundsCheck.svg"
|
||||
dest_files=["res://.godot/imported/BoundsCheck.svg-02b170ef6b912468a1f094b439993793.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="Carousel.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="45.254834"
|
||||
inkscape:cx="6.2755727"
|
||||
inkscape:cy="7.5461552"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect1"
|
||||
style="fill:#8eef97;stroke-width:1.00983;stroke-linecap:round;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="M 6.5878906,3.25 C 6.2619664,3.25 6,3.5053007 6,3.8222656 V 6.3339844 C 5.4677874,6.4073837 4.9651034,6.5094218 4.5,6.6386719 V 6.2050781 C 4.467678,6.1981224 4.0475257,6.1077072 3.9277344,5.9472656 3.7899997,5.7627915 3.8365665,5.2825145 3.8398438,5.25 H 1.0878906 C 0.7619663,5.25 0.5,5.5053007 0.5,5.8222656 V 12.177734 C 0.5,12.494699 0.7619663,12.75 1.0878906,12.75 H 3.9121094 C 4.2380335,12.75 4.5,12.494699 4.5,12.177734 V 8.2050781 C 4.933584,8.0597122 5.440452,7.9372271 6,7.8496094 V 10.177734 C 6,10.494699 6.2619664,10.75 6.5878906,10.75 H 9.4121094 C 9.738034,10.75 10,10.494699 10,10.177734 V 7.8496094 c 0.559262,0.087319 1.066077,0.2086506 1.5,0.3535156 v 3.974609 c 0,0.316965 0.261966,0.572266 0.587891,0.572266 h 2.824218 C 15.238034,12.75 15.5,12.494699 15.5,12.177734 V 6.2050781 C 15.46768,6.1981181 15.047526,6.1077071 14.927734,5.9472656 14.790001,5.7627915 14.836564,5.2825145 14.839844,5.25 H 12.087891 C 11.761966,5.25 11.5,5.5053007 11.5,5.8222656 v 0.8125 C 11.034934,6.5061128 10.531913,6.4049131 10,6.3320312 V 4.2050781 C 9.967678,4.1981224 9.547526,4.1077072 9.4277344,3.9472656 9.2899996,3.7627915 9.3365668,3.2825145 9.3398438,3.25 Z M 9.34375,3.25 10,4.1992188 V 3.8222656 C 10,3.5053007 9.738034,3.25 9.4121094,3.25 Z m -5.5,2 L 4.5,6.1992188 V 5.8222656 C 4.5,5.5053007 4.2380335,5.25 3.9121094,5.25 Z m 11,0 L 15.5,6.1992188 V 5.8222656 C 15.5,5.5053007 15.238034,5.25 14.912109,5.25 Z" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://vu8og3rtsflq"
|
||||
path="res://.godot/imported/Carousel.svg-24a12fa029ec5088ae1cb278064dea40.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/Carousel.svg"
|
||||
dest_files=["res://.godot/imported/Carousel.svg-24a12fa029ec5088ae1cb278064dea40.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="AnimatableMount.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="9.765625"
|
||||
inkscape:cy="6.140625"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1"></tspan></text>
|
||||
<path
|
||||
id="path4"
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;paint-order:markers fill stroke"
|
||||
d="M 8 0 A 2 2 0 0 0 6.0136719 1.8125 C 5.1236507 2.0994171 4.3162059 2.5728088 3.6386719 3.1875 A 2 2 0 0 0 1.0722656 4 A 2 2 0 0 0 1.6484375 6.6269531 C 1.5524645 7.0701586 1.5 7.5287197 1.5 8 C 1.5 8.4712803 1.5524645 8.9298414 1.6484375 9.3730469 A 2 2 0 0 0 1.0722656 12 A 2 2 0 0 0 3.6386719 12.8125 C 4.3162059 13.427191 5.1236507 13.900583 6.0136719 14.1875 A 2 2 0 0 0 8 16 A 2 2 0 0 0 9.9863281 14.1875 C 10.876349 13.900583 11.683794 13.427191 12.361328 12.8125 A 2 2 0 0 0 14.927734 12 A 2 2 0 0 0 14.353516 9.3691406 C 14.448906 8.9272161 14.5 8.4698082 14.5 8 C 14.5 7.5301919 14.448906 7.0727838 14.353516 6.6308594 A 2 2 0 0 0 14.927734 4 A 2 2 0 0 0 12.361328 3.1875 C 11.684844 2.5737618 10.878619 2.1015051 9.9902344 1.8144531 A 2 2 0 0 0 8 0 z M 6.4414062 3.2480469 A 2 2 0 0 0 8 4 A 2 2 0 0 0 9.5585938 3.2480469 C 10.224874 3.465094 10.828645 3.8154837 11.337891 4.2714844 A 2 2 0 0 0 11.464844 6 A 2 2 0 0 0 12.894531 6.9726562 C 12.963279 7.3042234 13 7.6474435 13 8 C 13 8.3518923 12.963027 8.6944022 12.894531 9.0253906 A 2 2 0 0 0 11.464844 10 A 2 2 0 0 0 11.337891 11.728516 C 10.829544 12.183711 10.227385 12.534811 9.5625 12.751953 A 2 2 0 0 0 8 12 A 2 2 0 0 0 6.4414062 12.751953 C 5.7760209 12.535198 5.1728779 12.185579 4.6640625 11.730469 A 2 2 0 0 0 4.5351562 10 A 2 2 0 0 0 3.1054688 9.0273438 C 3.0367209 8.6957766 3 8.3525566 3 8 C 3 7.6481077 3.0369732 7.3055978 3.1054688 6.9746094 A 2 2 0 0 0 4.5351562 6 A 2 2 0 0 0 4.6640625 4.2695312 C 5.1728779 3.8144207 5.7760209 3.4648024 6.4414062 3.2480469 z " />
|
||||
<g
|
||||
id="g7" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c00ttqalyh2ck"
|
||||
path="res://.godot/imported/CircularContainer.svg-1dd941875614a9abaf152e4d2a3a686b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/CircularContainer.svg"
|
||||
dest_files=["res://.godot/imported/CircularContainer.svg-1dd941875614a9abaf152e4d2a3a686b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="DistanceCheck.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="7.515625"
|
||||
inkscape:cy="7.171875"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<path
|
||||
id="path2"
|
||||
style="fill:#8eef97;fill-opacity:1;stroke-width:0.0412133;stroke-dasharray:none"
|
||||
d="M 8 0.5 C 3.8639067 0.5 0.5 3.8639067 0.5 8 C 0.49999994 12.136094 3.8639067 15.5 8 15.5 C 12.136094 15.5 15.5 12.136094 15.5 8 C 15.5 3.8639067 12.136094 0.49999994 8 0.5 z M 8 1.5195312 C 11.585459 1.5195311 14.480469 4.4145407 14.480469 8 C 14.480469 11.585459 11.585459 14.480469 8 14.480469 C 6.3969877 14.480469 4.9327997 13.901831 3.8027344 12.941406 L 11.394531 9.90625 C 11.401231 9.90395 11.408353 9.9013375 11.414062 9.8984375 L 12.003906 9.609375 L 12.289062 9.46875 C 12.292462 9.46735 12.295938 9.4669437 12.298828 9.4648438 C 12.374538 9.4230638 12.428933 9.3534097 12.455078 9.2773438 C 12.481918 9.1991138 12.4375 9.0703125 12.4375 9.0703125 C 12.4375 9.0703125 11.675644 9.4407963 11.294922 9.6269531 L 11.292969 9.6269531 C 11.290369 9.6278251 11.285156 9.6328125 11.285156 9.6328125 C 11.285156 9.6328125 11.267856 9.6336594 11.259766 9.6308594 C 11.252766 9.6287594 11.249104 9.6237406 11.246094 9.6191406 L 10.433594 7.9746094 C 10.424594 7.9561994 10.411014 7.9383712 10.396484 7.9257812 C 10.381944 7.9131913 10.365606 7.9045175 10.347656 7.8984375 C 10.329706 7.8923375 10.310206 7.8875719 10.291016 7.8886719 C 10.271826 7.8897719 10.252645 7.8953069 10.234375 7.9042969 C 10.220065 7.9115969 10.206532 7.9208106 10.195312 7.9316406 L 8.859375 9.2480469 C 8.817075 9.2896168 8.7729127 9.3248407 8.7304688 9.3535156 C 8.6879788 9.3817456 8.6466558 9.4051688 8.6074219 9.4179688 C 8.5690419 9.4302687 8.5341256 9.4348644 8.5097656 9.4277344 C 8.5022656 9.4256344 8.4989575 9.4181694 8.4921875 9.4121094 C 8.4854875 9.4061094 8.4780362 9.400645 8.4726562 9.390625 C 8.4633563 9.373435 8.4548187 9.3497056 8.4492188 9.3222656 C 8.4436188 9.2948257 8.4417062 9.2641125 8.4414062 9.2265625 C 8.4213263 7.260155 8.4148025 5.2923576 8.3828125 3.3261719 C 8.3828125 3.3261719 8.3174012 3.3234788 8.3007812 3.3242188 C 8.2841613 3.3249538 8.26659 3.3276413 8.25 3.3320312 C 8.23322 3.3353012 8.2162019 3.3415345 8.2011719 3.3515625 C 8.1811319 3.3640435 8.1673037 3.3808965 8.1523438 3.3984375 C 8.1451438 3.4012075 8.1376669 3.4087002 8.1230469 3.4394531 C 7.1026661 5.5858654 4.4022755 10.22464 3.1699219 12.322266 C 2.1439516 11.176153 1.5195312 9.6632327 1.5195312 8 C 1.5195312 4.4145407 4.4145407 1.5195313 8 1.5195312 z M 8.7089844 2.5371094 L 8.7617188 8.4765625 C 8.7619187 8.5169265 8.7654844 8.5525413 8.7714844 8.5820312 C 8.7775144 8.6115212 8.7849019 8.6338638 8.7949219 8.6523438 C 8.8007219 8.6631237 8.8071531 8.6712344 8.8144531 8.6777344 C 8.8217331 8.6842644 8.8296906 8.6891063 8.8378906 8.6914062 C 8.8640726 8.6991362 8.9001563 8.6948606 8.9414062 8.6816406 C 8.9835782 8.6678906 9.0265857 8.6436312 9.0722656 8.6132812 C 9.1178896 8.5824603 9.1654775 8.54468 9.2109375 8.5 L 10.267578 7.4667969 C 10.279648 7.4551539 10.293224 7.44536 10.308594 7.4375 C 10.328234 7.427861 10.348511 7.4211119 10.369141 7.4199219 C 10.389761 7.4187329 10.412351 7.4231475 10.431641 7.4296875 C 10.450941 7.4362275 10.466792 7.4454534 10.482422 7.4589844 C 10.498042 7.4725134 10.511884 7.4899756 10.521484 7.5097656 L 11.396484 9.28125 C 11.399714 9.28615 11.402656 9.2887156 11.410156 9.2910156 C 11.418856 9.2940926 11.4291 9.2953687 11.4375 9.2929688 C 11.44 9.2916688 11.442613 9.2899495 11.445312 9.2890625 L 11.447266 9.2890625 L 12.376953 8.8339844 C 12.385643 8.8286444 12.388978 8.8198469 12.392578 8.8105469 C 12.394919 8.8029869 12.398484 8.7959156 12.396484 8.7910156 L 11.529297 7.015625 L 11.53125 7.015625 C 11.52391 7.000887 11.518025 6.9822137 11.515625 6.9648438 C 11.512425 6.9420398 11.515384 6.9231047 11.521484 6.9023438 C 11.532814 6.8638018 11.557157 6.8291457 11.591797 6.8085938 C 11.609567 6.7980488 11.630624 6.7920325 11.652344 6.7890625 L 13.111328 6.5878906 C 13.113128 6.5874222 13.114788 6.5877392 13.117188 6.5878906 C 13.242998 6.5703231 13.423828 6.4824219 13.423828 6.4824219 C 13.459938 6.4575769 13.486064 6.4336311 13.496094 6.4082031 C 13.502514 6.3926161 13.500554 6.3722664 13.490234 6.3496094 C 13.472717 6.3104426 13.436127 6.2667718 13.373047 6.2167969 L 8.7089844 2.5371094 z " />
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://den188fn44ahq"
|
||||
path="res://.godot/imported/DistanceCheck.svg-cc9439b9bfdf47f875ceab8615f27948.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/DistanceCheck.svg"
|
||||
dest_files=["res://.godot/imported/DistanceCheck.svg-cc9439b9bfdf47f875ceab8615f27948.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
62
godot/addons/FreeControl/assets/icons/CustomType/Drawer.svg
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="Drawer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="9.640625"
|
||||
inkscape:cy="6.421875"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect2"
|
||||
style="fill:#8eef97;stroke-width:11.0382;stroke-linecap:round;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="m 9.032595,1.75 c -0.5540498,-1e-7 -0.9991024,0.4491454 -0.9991024,1.0058593 V 3.296875 H 10.607057 V 3.8925781 H 8.0334926 V 6.9492186 H 5.9128288 4.9798145 v -0.5 l -2.5813397,1.5 2.5813397,1.5000004 v -0.5 h 0.9330143 2.1206638 v 4.296874 C 8.0334925,13.802809 8.4785452,14.25 9.032595,14.25 H 9.885915 13.998953 15 V 13.246093 2.7558593 1.75 H 13.998953 9.885915 Z M 4.9798145,7.6992186 h 0.9330143 2.1206638 v 0.5 H 5.9128288 4.9798145 Z"
|
||||
sodipodi:nodetypes="sscccccccccccccssccccccccsccccccc" />
|
||||
<g
|
||||
id="path4"
|
||||
transform="matrix(0.99521529,0,0,0.99999998,-0.23534689,0.2695312)">
|
||||
<g
|
||||
id="g13">
|
||||
<g
|
||||
id="path13">
|
||||
<path
|
||||
style="color:#000000;fill:#8eef97;fill-rule:evenodd;-inkscape-stroke:none"
|
||||
d="M 5.4414062,5.8320312 2.2460938,7.6796875 5.4414062,9.5253906 Z M 5.0410156,6.5253906 V 8.8320313 L 3.0449219,7.6796875 Z"
|
||||
id="path15" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://lje075l441hl"
|
||||
path="res://.godot/imported/Drawer.svg-7020a44fe2fbf3bc7e577f6647e28243.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/Drawer.svg"
|
||||
dest_files=["res://.godot/imported/Drawer.svg-7020a44fe2fbf3bc7e577f6647e28243.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="HoldButton.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="7.578125"
|
||||
inkscape:cy="11.109375"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g2" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)">
|
||||
<path
|
||||
id="path1-9"
|
||||
style="fill:#8eef97;fill-opacity:1;stroke:none;stroke-width:16.2328;stroke-opacity:1"
|
||||
d="M 2.2392589,7.2640805 V 58.118343 L -21.22222,42.456248 -39.23049,69.468613 9.4679308,101.93418 c 5.4548142,3.63938 12.5534562,3.63938 18.0082702,0 L 76.174622,69.468613 58.166352,42.456248 34.704873,58.118343 V 7.2640805 Z M -46.7128,129.2953 c -6.063399,0 -10.938122,4.87556 -10.938122,10.9381 v 5.70684 h -14.267115 c -3.878694,0 -7.006739,3.12855 -7.006739,7.00672 v 18.42042 7.00672 H 115.86891 v -24.06383 -1.36331 c 0,-3.87817 -3.12803,-7.00672 -7.00674,-7.00672 H 94.595054 v -5.70684 c 0,-6.06254 -4.874718,-10.9381 -10.938122,-10.9381 h -1.648645 v 9.89184 a 63.536855,19.02034 0 0 1 -4.280134,6.7531 H -40.784021 a 63.536855,19.02034 0 0 1 -4.280134,-6.7531 v -9.89184 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ceqkp35yu0v27"
|
||||
path="res://.godot/imported/HoldButton.svg-059d8d0ebedf00f7d7ee555447e9ac85.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/HoldButton.svg"
|
||||
dest_files=["res://.godot/imported/HoldButton.svg-059d8d0ebedf00f7d7ee555447e9ac85.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="MaxRatioContainer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="7.140625"
|
||||
inkscape:cy="6.953125"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="path76"
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:square;stroke-miterlimit:3.9;stroke-dasharray:18.0028, 18.0028;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 0 0 L 0 10.75 L 1.5 10.75 L 1.5 2.9277344 L 7.7480469 9.1757812 L 3.8886719 9.1757812 L 3.8886719 10.589844 L 10.589844 10.589844 L 10.589844 3.8886719 L 9.1757812 3.8886719 L 9.1757812 7.7480469 L 2.9277344 1.5 L 10.251953 1.5 L 10.251953 0 L 0 0 z M 15 9.75 L 15 14.310547 L 12.126953 11.4375 L 13.992188 11.4375 L 13.992188 10.753906 L 10.753906 10.753906 L 10.753906 13.992188 L 11.4375 13.992188 L 11.4375 12.126953 L 14.310547 15 L 9.75 15 L 9.75 16 L 16 16 L 16 9.75 L 15 9.75 z " />
|
||||
<g
|
||||
id="path78">
|
||||
<g
|
||||
id="g89" />
|
||||
</g>
|
||||
<g
|
||||
id="path79">
|
||||
<g
|
||||
id="g91" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://crwd74vrtbrmm"
|
||||
path="res://.godot/imported/MaxRatioContainer.svg-69252897895331776d794bb3e7d4450a.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/MaxRatioContainer.svg"
|
||||
dest_files=["res://.godot/imported/MaxRatioContainer.svg-69252897895331776d794bb3e7d4450a.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="MaxSizeContainer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="5.578125"
|
||||
inkscape:cy="8.046875"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="path78">
|
||||
<g
|
||||
id="g85" />
|
||||
</g>
|
||||
<g
|
||||
id="path79">
|
||||
<g
|
||||
id="g87">
|
||||
<path
|
||||
id="path87"
|
||||
style="color:#000000;fill:#8eef97;-inkscape-stroke:none"
|
||||
d="M 8.0664063,8.0664063 V 13.046875 H 9.1171875 V 9.1171875 H 13.046875 V 8.0664063 Z M 9,8.25 a 0.75,0.75 0 0 0 -0.53125,0.21875 0.75,0.75 0 0 0 0,1.0625 l 6,6 a 0.75,0.75 0 0 0 1.0625,0 0.75,0.75 0 0 0 0,-1.0625 l -6,-6 A 0.75,0.75 0 0 0 9,8.25 Z m 5.5,-3 V 14.5 H 5.7480469 V 16 H 16 V 5.25 Z M 6.8828125,2.953125 V 6.8828125 H 2.953125 V 7.9335937 H 7.9335937 V 2.953125 Z M 1,0.25 a 0.75,0.75 0 0 0 -0.53125,0.21875 0.75,0.75 0 0 0 0,1.0625 l 6,6 a 0.75,0.75 0 0 0 1.0625,0 0.75,0.75 0 0 0 0,-1.0625 l -6,-6 A 0.75,0.75 0 0 0 1,0.25 Z M 0,0 V 10.75 H 1.5 V 1.5 h 8.751953 V 0 Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cj5vpwdi8b2t4"
|
||||
path="res://.godot/imported/MaxSizeContainer.svg-c449fe736ac19e4da4954b352d7a4bc0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/MaxSizeContainer.svg"
|
||||
dest_files=["res://.godot/imported/MaxSizeContainer.svg-c449fe736ac19e4da4954b352d7a4bc0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="ModulateTransitionButton.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="5.9883105"
|
||||
inkscape:cy="11.159029"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)"
|
||||
style="fill:#8eef97;fill-opacity:1">
|
||||
<path
|
||||
id="path1"
|
||||
style="stroke-width:12.3099"
|
||||
d="m -39.797083,0.42818857 c -6.641652,0 -11.973645,5.33175813 -11.973645,11.97305343 V 132.61263 c 0,6.64128 5.331993,11.97305 11.973645,11.97305 h 21.951681 v 6.90014 H -42.465907 V 176.1051 H 105.25712 v -24.61928 h -24.6205 v -6.90014 h 21.95168 c 6.64166,0 11.97364,-5.33177 11.97364,-11.97305 V 12.401242 c 0,-6.6412953 -5.33198,-11.97305343 -11.97364,-11.97305343 z M -25.924005,17.065444 H 88.715224 c 5.10896,0 9.20865,4.123546 9.20865,9.232234 v 92.442552 c 0,5.10869 -4.09969,9.2082 -9.20865,9.2082 H 79.891272 C 77.682194,120.22478 70.604751,114.55688 62.171241,114.55688 H 0.61997721 c -8.43351751,0 -15.51095321,5.6679 -17.72003221,13.39155 h -8.82395 c -5.108962,0 -9.208647,-4.09951 -9.208647,-9.2082 V 26.297678 c 0,-5.108688 4.099685,-9.232234 9.208647,-9.232234 z M -12.050928,33.7027 c -3.57628,0 -6.467691,2.867249 -6.467691,6.44333 v 6.515457 L -4.6455408,33.7027 Z m 23.634724,0 -30.102415,28.177548 V 79.911956 L 30.842609,33.7027 Z m 35.464107,0 -65.566522,61.403971 v 9.761169 c 0,2.03366 0.943408,3.8193 2.404347,5.00079 L 65.393065,33.7027 Z M 79.506577,35.698209 -1.3996738,111.33521 H 16.729097 L 81.309837,50.844843 V 40.14603 c 0,-1.737697 -0.693978,-3.289675 -1.80326,-4.447821 z m 1.80326,30.365396 -48.327359,45.271605 H 51.135291 L 81.309837,83.061495 Z m 0,32.192609 -13.969252,13.078996 h 7.50156 c 3.576279,0 6.467692,-2.89129 6.467692,-6.46737 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bf8ifq48pj2iv"
|
||||
path="res://.godot/imported/ModulateTransitionButton.svg-124cc46ae542378e7b346986eb52d0c7.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/ModulateTransitionButton.svg"
|
||||
dest_files=["res://.godot/imported/ModulateTransitionButton.svg-124cc46ae542378e7b346986eb52d0c7.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="ModulateTransitionContainer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="6.75"
|
||||
inkscape:cy="10.203125"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)"
|
||||
style="fill:#8eef97;fill-opacity:1">
|
||||
<path
|
||||
id="rect7"
|
||||
style="stroke-width:3.05337;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="m -16.834572,45.181424 c -3.969758,0 -7.164803,3.195084 -7.164803,7.164842 v 7.236971 L -8.6118792,45.181424 Z m 26.2308716,0 -33.3956746,31.280064 v 20.027896 l 54.769868,-51.30796 z m 39.3823724,0 -72.778047,68.162166 v 10.84344 c 0,2.25755 1.047085,4.2424 2.668769,5.55395 L 69.143061,45.181424 Z M 84.79503,47.393389 -5.0054349,131.35187 H 15.118525 L 86.790596,64.223553 V 52.346266 c 0,-1.928993 -0.764236,-3.667234 -1.995566,-4.952877 z m 1.995566,33.708415 -53.63985,50.250066 H 53.298749 L 86.790596,99.975632 Z m 0,35.752076 -15.483668,14.49799 h 8.318865 c 3.969759,0 7.164803,-3.19509 7.164803,-7.16484 z M -47.633607,8.2512339 c -7.372408,0 -13.295758,5.9234221 -13.295758,13.2958301 V 154.98623 c 0,7.3724 5.92335,13.29583 13.295758,13.29583 H 110.42483 c 7.37241,0 13.29576,-5.92343 13.29576,-13.29583 V 21.547064 c 0,-7.372408 -5.92335,-13.2958301 -13.29576,-13.2958301 z M -32.222068,26.716329 H 95.013289 c 5.671081,0 10.242301,4.571275 10.242301,10.242357 V 139.57461 c 0,5.67108 -4.57122,10.24235 -10.242301,10.24235 H -32.222068 c -5.671083,0 -10.242302,-4.57127 -10.242302,-10.24235 V 36.958686 c 0,-5.671082 4.571219,-10.242357 10.242302,-10.242357 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://s3hp67f43n0x"
|
||||
path="res://.godot/imported/ModulateTransitionContainer.svg-41f98f4655621d9ed04421a4b13baee4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/ModulateTransitionContainer.svg"
|
||||
dest_files=["res://.godot/imported/ModulateTransitionContainer.svg-41f98f4655621d9ed04421a4b13baee4.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="MotionCheck.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="45.254834"
|
||||
inkscape:cx="6.8169513"
|
||||
inkscape:cy="4.5409514"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)"
|
||||
style="fill:#8eef97;fill-opacity:1">
|
||||
<path
|
||||
id="path2-5"
|
||||
style="fill:#8eef97;fill-opacity:1;stroke-width:0.733503;stroke-dasharray:none"
|
||||
d="m 40.131733,21.02908 57.407945,45.283565 c 0.77651,0.615195 1.23543,1.15024 1.45106,1.632386 0.12704,0.278909 0.13935,0.533628 0.0603,0.725505 -0.12347,0.313023 -0.4322,0.601043 -0.87672,0.906883 0,0 -2.23007,1.08361 -3.77881,1.29986 -0.0295,-0.002 -0.0689,-0.006 -0.0911,0 l -17.95703,2.47881 c -0.26738,0.0366 -0.50681,0.11202 -0.72555,0.24183 -0.42642,0.253 -0.73725,0.67426 -0.87672,1.14871 -0.0751,0.25557 -0.0997,0.50523 -0.0603,0.78595 0.0295,0.21383 0.0911,0.42316 0.18145,0.60459 h -0.0308 l 10.67141,21.8558 c 0.0246,0.0603 -0.002,0.14883 -0.0308,0.2419 -0.0443,0.11448 -0.10463,0.23647 -0.21161,0.30221 l -11.42718,5.592411 h -0.0308 c -0.0332,0.0109 -0.0603,0.0443 -0.0911,0.0603 -0.10341,0.0295 -0.2254,0.007 -0.3325,-0.0308 -0.0923,-0.0283 -0.14169,-0.0603 -0.18145,-0.12064 L 62.44194,82.243729 c -0.118176,-0.24362 -0.291377,-0.46828 -0.483659,-0.63482 -0.192406,-0.16657 -0.397367,-0.28226 -0.63495,-0.36277 -0.23746,-0.0805 -0.501879,-0.13552 -0.755711,-0.12088 -0.253955,0.0147 -0.513942,0.0929 -0.75571,0.2116 -0.189205,0.0968 -0.365361,0.21942 -0.513943,0.36275 l -12.999184,12.72652 c -0.559613,0.55002 -1.131289,1.01115 -1.69292,1.39056 -0.562321,0.37361 -1.113316,0.67718 -1.632454,0.84644 -0.507787,0.16274 -0.947377,0.2158 -1.269677,0.12064 -0.100942,-0.0283 -0.182558,-0.10094 -0.272174,-0.18133 -0.08986,-0.08 -0.170494,-0.16963 -0.241892,-0.30233 -0.123346,-0.22749 -0.227981,-0.51358 -0.30221,-0.8766 -0.07386,-0.36303 -0.118176,-0.80299 -0.120638,-1.29987 z m 31.819645,87.26269 h -0.0222 c -0.032,0.0107 -0.0849,0.064 -0.0849,0.064 0,0 -0.22035,0.0135 -0.31994,-0.0209 -0.0862,-0.0258 -0.13368,-0.0714 -0.17074,-0.12802 L 61.351139,87.944539 c -0.11079,-0.22663 -0.268973,-0.44218 -0.447837,-0.59716 -0.178988,-0.15498 -0.376317,-0.26639 -0.597281,-0.34123 -0.220965,-0.0751 -0.467534,-0.12064 -0.703763,-0.1071 -0.236229,0.0135 -0.478859,0.0812 -0.703762,0.19191 -0.176157,0.0899 -0.331016,0.20792 -0.469134,0.34124 L 41.965959,103.63414 c -0.520713,0.51172 -1.055828,0.94799 -1.578314,1.30099 -0.523052,0.34751 -1.031331,0.63162 -1.514302,0.78919 -0.472457,0.15142 -0.894567,0.19487 -1.194439,0.1071 -0.09232,-0.0259 -0.15129,-0.096 -0.234628,-0.17062 -0.08248,-0.0739 -0.168278,-0.15387 -0.234506,-0.27722 -0.114483,-0.21161 -0.208285,-0.494 -0.277221,-0.83179 -0.06894,-0.33779 -0.103404,-0.73208 -0.107097,-1.19432 -0.247185,-24.206631 -0.331385,-48.416768 -0.725182,-72.620663 0,0 -0.797934,-0.03041 -1.002526,-0.0213 -0.204592,0.009 -0.414354,0.03127 -0.618577,0.08531 -0.206562,0.04025 -0.412139,0.11116 -0.597158,0.234605 -0.246693,0.153642 -0.43442,0.359909 -0.618578,0.57584 -0.08863,0.0341 -0.182557,0.133281 -0.362529,0.511853 C 16.520372,66.58041 -36.309372,155.50421 -36.309372,155.50421 l 109.49778,-43.77868 c 0.0825,-0.0283 0.16434,-0.0492 0.23463,-0.0849 l 7.25165,-3.56168 3.51919,-1.72755 c 0.0419,-0.0172 0.0923,-0.0172 0.1279,-0.0431 0.93199,-0.51431 1.59774,-1.38834 1.91958,-2.32472 0.33041,-0.96301 -0.21333,-2.53799 -0.21333,-2.53799 0,0 -9.39005,4.55462 -14.07675,6.84622 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://jywj8x8u5pop"
|
||||
path="res://.godot/imported/MotionCheck.svg-5facd6c8a5eea77e7e6606c52a8d456d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/MotionCheck.svg"
|
||||
dest_files=["res://.godot/imported/MotionCheck.svg-5facd6c8a5eea77e7e6606c52a8d456d.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="PaddingContainer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="1.421875"
|
||||
inkscape:cy="7.765625"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect35"
|
||||
style="color:#000000;fill:#8eef97;stroke-width:0.5;stroke-linecap:round;stroke-miterlimit:3.9;stroke-dasharray:none;paint-order:stroke fill markers"
|
||||
d="M 1,1 V 15 H 15 V 1 Z M 2,2 H 4.9697265 7.9394531 L 7.1601562,2.7792969 V 3.3398438 L 8,2.5 8.8398438,3.3398438 V 2.7792969 L 8.0605469,2 H 14 V 7.9394531 L 13.220703,7.1601562 H 12.660156 L 13.5,8 12.660156,8.8398438 h 0.560547 L 14,8.0605469 V 14 H 2 V 8.0605469 L 2.7792969,8.8398438 H 3.3398438 L 2.5,8 3.3398438,7.1601562 H 2.7792969 L 2,7.9394531 Z m 2,2 v 8 h 3.8007812 v 1.5 0.199219 H 8.1992188 V 13.5 12 H 12 V 4 Z m 1,1 h 6 v 6 H 5 Z"
|
||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccc" />
|
||||
<g
|
||||
id="path36">
|
||||
<g
|
||||
id="g2" />
|
||||
</g>
|
||||
<g
|
||||
id="path3">
|
||||
<path
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:square;stroke-miterlimit:3.9;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 2.3007813,7.8007812 V 8.1992188 H 2.5 4 4.1992187 V 7.8007812 H 4 2.5 Z"
|
||||
id="path11" />
|
||||
<g
|
||||
id="g10">
|
||||
<path
|
||||
style="color:#000000;fill:#8eef97;fill-rule:evenodd;-inkscape-stroke:none"
|
||||
d="M 1.94,8 2.78,7.16 H 3.34 L 2.5,8 3.34,8.84 H 2.78 Z"
|
||||
id="path10" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="path6">
|
||||
<path
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:square;stroke-miterlimit:3.9;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 2.3007813,7.8007812 V 8.1992188 H 2.5 4 4.1992187 V 7.8007812 H 4 2.5 Z"
|
||||
id="path13" />
|
||||
<g
|
||||
id="g12" />
|
||||
</g>
|
||||
<g
|
||||
id="path7">
|
||||
<path
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:square;stroke-miterlimit:3.9;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 11.800781,7.8007812 V 8.1992188 H 12 13.5 13.699219 V 7.8007812 H 13.5 12 Z"
|
||||
id="path15" />
|
||||
<g
|
||||
id="g14" />
|
||||
</g>
|
||||
<g
|
||||
id="path8">
|
||||
<path
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:square;stroke-miterlimit:3.9;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 7.8007812,2.3007813 V 2.5 4 4.1992187 H 8.1992188 V 4 2.5 2.3007813 Z"
|
||||
id="path17" />
|
||||
<g
|
||||
id="g16" />
|
||||
</g>
|
||||
<g
|
||||
id="path9">
|
||||
<g
|
||||
id="g18">
|
||||
<path
|
||||
style="color:#000000;fill:#8eef97;fill-rule:evenodd;-inkscape-stroke:none"
|
||||
d="M 8,14.06 7.16,13.22 V 12.66 L 8,13.5 8.84,12.66 v 0.56 z"
|
||||
id="path18" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c6o6frklk2afj"
|
||||
path="res://.godot/imported/PaddingContainer.svg-9d29b77cc431f8e4258d6655803790fe.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/PaddingContainer.svg"
|
||||
dest_files=["res://.godot/imported/PaddingContainer.svg-9d29b77cc431f8e4258d6655803790fe.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
51
godot/addons/FreeControl/assets/icons/CustomType/Page.svg
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="Page.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="0.99436891"
|
||||
inkscape:cy="8.1980192"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g2" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)">
|
||||
<path
|
||||
id="rect11"
|
||||
style="color:#000000;fill:#8eef97;fill-opacity:1;stroke-width:0.959576;stroke-miterlimit:3.9;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="m -43.178001,-22.076718 c -12.938746,0 -23.588403,11.565574 -23.588403,25.6169459 V 182.09795 c 0,14.05138 10.649657,25.61695 23.588403,25.61695 H 80.122133 c 12.93874,0 23.588397,-3.3225 23.588397,-17.37388 V 28.269459 L 52.475512,-22.076718 H 48.86116 36.306042 34.150113 Z m 0,16.2325202 H 36.306042 V 42.536322 H 88.777563 V 182.09795 c 0,5.33935 -3.73886,9.38442 -8.65543,9.38442 H -43.178001 c -4.916566,0 -8.655422,-4.04507 -8.655422,-9.38442 V 3.5402279 c 0,-5.33935 3.738856,-9.3844257 8.655422,-9.3844257 z M 48.86116,-4.6394401 85.543663,29.981481 H 48.86116 Z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cqtdlpg3h0j5d"
|
||||
path="res://.godot/imported/Page.svg-d8188a8e86ac732bd815acd1e14bc1cd.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/Page.svg"
|
||||
dest_files=["res://.godot/imported/Page.svg-d8188a8e86ac732bd815acd1e14bc1cd.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M1.553 4.104A1 1 0 0 0 1 5v6a1 1 0 0 0 .553.895l6 3a1 1 0 0 0 .894 0l6-3A1 1 0 0 0 15 11V5a1 1 0 0 0-.553-.894l-6-3a1 1 0 0 0-.894 0zm6.447-1 3.764 1.882L8 6.868 4.236 4.986zm-5 3.5 4 2v3.766l-4-2z"/></svg>
|
After Width: | Height: | Size: 294 B |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b2wlsev3wn5rq"
|
||||
path="res://.godot/imported/PageInfo.svg-cac5d98635bfc001a76d9044e0d773df.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/PageInfo.svg"
|
||||
dest_files=["res://.godot/imported/PageInfo.svg-cac5d98635bfc001a76d9044e0d773df.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="ProportionalContainer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="3.734375"
|
||||
inkscape:cy="6.046875"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<path
|
||||
id="rect35"
|
||||
style="color:#000000;fill:#8eef97;stroke-linecap:round;stroke-miterlimit:3.9;-inkscape-stroke:none;paint-order:stroke fill markers"
|
||||
d="M 1 1 L 1 6 L 1 15 L 10 15 L 15 15 L 15 1 L 1 1 z M 2.234375 2.234375 L 12.988281 2.234375 L 11.111328 4.1113281 L 11.111328 2.5546875 L 10.333984 3.3339844 L 10.333984 5.6660156 L 12.666016 5.6660156 L 13.445312 4.8886719 L 11.888672 4.8886719 L 13.765625 3.0117188 L 13.765625 13.765625 L 10 13.765625 L 10 6 L 2.234375 6 L 2.234375 2.234375 z M 2.234375 7 L 9 7 L 9 13.765625 L 2.234375 13.765625 L 2.234375 7 z " />
|
||||
<g
|
||||
id="path36">
|
||||
<g
|
||||
id="g2" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d08yq8cd27f3f"
|
||||
path="res://.godot/imported/ProportionalContainer.svg-71fab0e839b225c75a1402a08ebf61b4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/ProportionalContainer.svg"
|
||||
dest_files=["res://.godot/imported/ProportionalContainer.svg-71fab0e839b225c75a1402a08ebf61b4.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="RouterStack.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="7.015625"
|
||||
inkscape:cy="7.078125"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g2" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)">
|
||||
<path
|
||||
id="rect13"
|
||||
style="fill:#8eef97;stroke-width:9.24964;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="m -57.302171,10.03989 c -2.980772,0 -5.389802,2.377268 -5.389802,5.35798 v 41.183523 c 0,2.98071 2.40903,5.38969 5.389802,5.38969 H 94.246302 c 2.98077,0 5.3898,-2.40898 5.3898,-5.38969 V 15.39787 c 0,-2.980712 -2.40903,-5.35798 -5.3898,-5.35798 z m 8.116404,56.813603 c -2.980775,0 -5.389799,2.37728 -5.389799,5.35799 v 41.183507 c 0,2.98072 2.409024,5.38969 5.389799,5.38969 H 102.3627 c 2.98078,0 5.3898,-2.40897 5.3898,-5.38969 V 72.211483 c 0,-2.98071 -2.40902,-5.35799 -5.3898,-5.35799 z M -65.418573,123.6671 c -2.98078,0 -5.3898,2.37726 -5.3898,5.35798 v 41.18352 c 0,2.98071 2.40902,5.38969 5.3898,5.38969 H 86.129902 c 2.98077,0 5.3898,-2.40898 5.3898,-5.38969 v -41.18352 c 0,-2.98072 -2.40903,-5.35798 -5.3898,-5.35798 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bx72t0x80xoc"
|
||||
path="res://.godot/imported/RouterStack.svg-943b2eec7eb910602fd6b012fee2113d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/RouterStack.svg"
|
||||
dest_files=["res://.godot/imported/RouterStack.svg-943b2eec7eb910602fd6b012fee2113d.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="StyleTransitionButton.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="5.9883105"
|
||||
inkscape:cy="11.159029"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)"
|
||||
style="fill:#8eef97;fill-opacity:1">
|
||||
<path
|
||||
id="rect7-8"
|
||||
style="fill:#8eef97;fill-opacity:1;stroke-width:2.56756;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="m -35.057656,3.61034 c -6.199321,0 -11.179728,4.980619 -11.179728,11.180114 V 127.00033 c 0,6.1995 4.980407,11.18012 11.179728,11.18012 h 17.214376 v 10.12221 h -24.619443 v 24.62029 H 105.25394 V 148.30266 H 80.634491 v -10.12221 h 17.21438 c 6.199329,0 11.179729,-4.98062 11.179729,-11.18012 V 14.790454 c 0,-6.199495 -4.9804,-11.180114 -11.179729,-11.180114 z m 12.958867,15.531944 h 106.98879 c 4.76871,0 8.60719,3.838633 8.60719,8.607486 v 86.29125 c 0,4.76883 -3.83848,8.63153 -8.60719,8.63153 h -5.69805 C 76.393813,116.03406 69.826169,111.37221 62.169913,111.37221 H 0.62130275 c -7.65626305,0 -14.22389875,4.66185 -17.02203675,11.30034 h -5.698055 c -4.768707,0 -8.607189,-3.8627 -8.607189,-8.63153 V 27.74977 c 0,-4.768853 3.838482,-8.607486 8.607189,-8.607486 z m 12.9348247,15.531944 c -3.3380987,0 -6.0106057,2.696662 -6.0106057,6.034857 v 60.396655 c 0,3.33821 2.672507,6.03487 6.0106057,6.03487 H 71.955181 c 3.338096,0 6.010606,-2.69666 6.010606,-6.03487 V 40.709085 c 0,-3.338195 -2.67251,-6.034857 -6.010606,-6.034857 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://beombqir8gvsg"
|
||||
path="res://.godot/imported/StyleTransitionButton.svg-31239487ac3b145d1d200758ae441fa7.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/StyleTransitionButton.svg"
|
||||
dest_files=["res://.godot/imported/StyleTransitionButton.svg-31239487ac3b145d1d200758ae441fa7.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="StyleTransitionContainer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="6.75"
|
||||
inkscape:cy="10.203125"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)"
|
||||
style="fill:#8eef97;fill-opacity:1">
|
||||
<path
|
||||
id="rect7"
|
||||
style="stroke-width:3.05337;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="M -16.833739,45.181423 H 79.62496 c 3.969763,0 7.165636,3.195874 7.165636,7.165636 v 71.839171 c 0,3.96976 -3.195873,7.16563 -7.165636,7.16563 h -96.458699 c -3.969762,0 -7.165635,-3.19587 -7.165635,-7.16563 V 52.347059 c 0,-3.969762 3.195873,-7.165636 7.165635,-7.165636 z M -47.633607,8.2512339 c -7.372408,0 -13.295758,5.9234221 -13.295758,13.2958301 V 154.98623 c 0,7.3724 5.92335,13.29583 13.295758,13.29583 H 110.42483 c 7.37241,0 13.29576,-5.92343 13.29576,-13.29583 V 21.547064 c 0,-7.372408 -5.92335,-13.2958301 -13.29576,-13.2958301 z M -32.222068,26.716329 H 95.013289 c 5.671081,0 10.242301,4.571275 10.242301,10.242357 V 139.57461 c 0,5.67108 -4.57122,10.24235 -10.242301,10.24235 H -32.222068 c -5.671083,0 -10.242302,-4.57127 -10.242302,-10.24235 V 36.958686 c 0,-5.671082 4.571219,-10.242357 10.242302,-10.242357 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c6dj30vpi468i"
|
||||
path="res://.godot/imported/StyleTransitionContainer.svg-d423c826296d72d4b9686de202d9c396.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/StyleTransitionContainer.svg"
|
||||
dest_files=["res://.godot/imported/StyleTransitionContainer.svg-d423c826296d72d4b9686de202d9c396.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="StyleTransitionPanel.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="6.75"
|
||||
inkscape:cy="10.203125"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)"
|
||||
style="fill:#8eef97;fill-opacity:1">
|
||||
<path
|
||||
id="rect7"
|
||||
style="stroke-width:18.46504659;stroke-miterlimit:3.9;paint-order:stroke fill markers;stroke:none;stroke-opacity:1;stroke-dasharray:73.86018637, 18.46504659;stroke-dashoffset:0;fill:#8eef97;fill-opacity:1"
|
||||
d="M -16.833739,45.181423 H 79.62496 c 3.969763,0 7.165636,3.195874 7.165636,7.165636 v 71.839171 c 0,3.96976 -3.195873,7.16563 -7.165636,7.16563 h -96.458699 c -3.969762,0 -7.165635,-3.19587 -7.165635,-7.16563 V 52.347059 c 0,-3.969762 3.195873,-7.165636 7.165635,-7.165636 z M -47.633607,8.2512339 c -7.372401,0 -13.295758,5.9234291 -13.295758,13.2958301 v 17.479328 h 18.464995 v -2.067706 c 0,-5.325214 4.045379,-9.634738 9.232497,-10.146185 V 8.2512339 Z m 32.866729,0 V 26.716329 H 3.6981175 V 8.2512339 Z m 36.929991,0 V 26.716329 H 40.628108 V 8.2512339 Z m 36.92999,0 V 26.716329 H 77.558098 V 8.2512339 Z m 36.92999,0 V 26.812501 c 5.187117,0.511447 9.232497,4.820971 9.232497,10.146185 v 2.067706 h 18.465 V 21.547064 c 0,-7.372401 -5.92336,-13.2958301 -13.29576,-13.2958301 z M -60.929365,57.491488 V 75.956583 H -42.46437 V 57.491488 Z m 166.184955,0 v 18.465095 h 18.465 V 57.491488 Z M -60.929365,94.421678 V 112.88677 H -42.46437 V 94.421678 Z m 166.184955,0 v 18.465092 h 18.465 V 94.421678 Z M -60.929365,131.35187 v 23.63436 c 0,7.37239 5.923357,13.29583 13.295758,13.29583 h 14.401734 v -18.56127 c -5.187118,-0.51145 -9.232497,-4.82097 -9.232497,-10.14618 v -8.22274 z m 166.184955,0 v 8.22274 c 0,5.32521 -4.04538,9.63473 -9.232497,10.14618 v 18.56127 h 14.401737 c 7.3724,0 13.29576,-5.92344 13.29576,-13.29583 v -23.63436 z m -120.022468,18.46509 v 18.4651 H 3.6981175 v -18.4651 z m 36.929991,0 v 18.4651 h 18.464995 v -18.4651 z m 36.92999,0 v 18.4651 h 18.464995 v -18.4651 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cbu1p6if3bhsq"
|
||||
path="res://.godot/imported/StyleTransitionPanel.svg-6e7803726b3f0f97dacbb20fcb7a611c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/StyleTransitionPanel.svg"
|
||||
dest_files=["res://.godot/imported/StyleTransitionPanel.svg-6e7803726b3f0f97dacbb20fcb7a611c.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="SwapContainer.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="7.765625"
|
||||
inkscape:cy="4.921875"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:13.3333px;font-family:Cambria;-inkscape-font-specification:Cambria;fill:#8654ce;stroke-width:5.66929;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.2;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="4.4930115"
|
||||
y="10.958069"
|
||||
id="text1"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan1" /></text>
|
||||
<g
|
||||
id="g7" />
|
||||
<g
|
||||
id="g2"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="matrix(0.06160364,0,0,0.06160364,6.8620535,2.2820062)" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.08123479,0,0,0.08123435,5.4495842,0.82971638)"
|
||||
style="fill:#8eef97;fill-opacity:1">
|
||||
<path
|
||||
id="rect1"
|
||||
style="fill:#8eef97;stroke-width:5.42346;stroke-miterlimit:3.9;paint-order:stroke fill markers"
|
||||
d="m 33.251393,6.2631586 c -17.890975,0 -34.14793266,4.0726514 -46.278476,10.9608624 -12.130545,6.888208 -20.529549,17.013486 -20.529549,28.938994 h -7.423114 l 12.033563,12.004754 11.801592,-12.004754 h -7.336124 c 0,-7.601178 5.543377,-15.143597 15.9480956,-21.051813 10.4047192,-5.908217 25.2873394,-9.771985 41.7840124,-9.771985 16.496674,0 31.379295,3.863768 41.784014,9.771985 10.404717,5.908216 15.948095,13.450635 15.948095,21.051813 h 9.075908 c 0,-11.925508 -8.398998,-22.050786 -20.529541,-28.938994 C 67.399325,10.33581 51.142371,6.2631586 33.251393,6.2631586 Z M -57.420784,61.24145 c -1.94727,0 -3.508581,1.561347 -3.508581,3.508637 V 166.76149 c 0,1.9473 1.561311,3.50864 3.508581,3.50864 H 0.05035604 c 1.94726956,0 3.50858196,-1.56134 3.50858196,-3.50864 V 64.750087 c 0,-1.94729 -1.5613124,-3.508637 -3.50858196,-3.508637 z m 120.161658,0 c -1.947269,0 -3.50858,1.561347 -3.50858,3.508637 V 166.76149 c 0,1.9473 1.561311,3.50864 3.50858,3.50864 h 57.471136 c 1.94727,0 3.50857,-1.56134 3.50857,-3.50864 V 64.750087 c 0,-1.94729 -1.5613,-3.508637 -3.50857,-3.508637 z" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,37 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b6ebfwbec45wl"
|
||||
path="res://.godot/imported/SwapContainer.svg-2f0195c9a4f5afd5ec3524f1349104ed.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/FreeControl/assets/icons/CustomType/SwapContainer.svg"
|
||||
dest_files=["res://.godot/imported/SwapContainer.svg-2f0195c9a4f5afd5ec3524f1349104ed.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
258
godot/addons/FreeControl/main.gd
Normal file
|
@ -0,0 +1,258 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
const GLOBAL_FOLDER := "res://addons/FreeControl/src/Other/Global/"
|
||||
const CUSTOM_CLASS_FOLDER := "res://addons/FreeControl/src/CustomClasses/"
|
||||
const ICON_FOLDER := "res://addons/FreeControl/assets/icons/CustomType/"
|
||||
|
||||
func _enter_tree() -> void:
|
||||
# AnimatableControls
|
||||
# Control
|
||||
add_custom_type(
|
||||
"AnimatableControl",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "AnimatableControl/control/AnimatableControl.gd"),
|
||||
load(ICON_FOLDER + "AnimatableControl.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"AnimatableScrollControl",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "AnimatableControl/control/AnimatableScrollControl.gd"),
|
||||
load(ICON_FOLDER + "AnimatableScrollControl.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"AnimatableZoneControl",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "AnimatableControl/control/AnimatableZoneControl.gd"),
|
||||
load(ICON_FOLDER + "AnimatableZoneControl.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"AnimatableVisibleControl",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "AnimatableControl/control/AnimatableVisibleControl.gd"),
|
||||
load(ICON_FOLDER + "AnimatableVisibleControl.svg")
|
||||
)
|
||||
# Mount
|
||||
add_custom_type(
|
||||
"AnimatableMount",
|
||||
"Control",
|
||||
load(CUSTOM_CLASS_FOLDER + "AnimatableControl/mount/AnimatableMount.gd"),
|
||||
load(ICON_FOLDER + "AnimatableMount.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"AnimatableTransformationMount",
|
||||
"Control",
|
||||
load(CUSTOM_CLASS_FOLDER + "AnimatableControl/mount/AnimatableTransformationMount.gd"),
|
||||
load(ICON_FOLDER + "AnimatableTransformationMount.svg")
|
||||
)
|
||||
|
||||
# Buttons
|
||||
# Base
|
||||
add_custom_type(
|
||||
"AnimatedSwitch",
|
||||
"BaseButton",
|
||||
load(CUSTOM_CLASS_FOLDER + "Buttons/Base/AnimatedSwitch.gd"),
|
||||
load(ICON_FOLDER + "AnimatedSwitch.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"HoldButton",
|
||||
"BaseButton",
|
||||
load(CUSTOM_CLASS_FOLDER + "Buttons/Base/HoldButton.gd"),
|
||||
load(ICON_FOLDER + "HoldButton.svg")
|
||||
)
|
||||
# MotionCheck
|
||||
add_custom_type(
|
||||
"BoundsCheck",
|
||||
"Control",
|
||||
load(CUSTOM_CLASS_FOLDER + "Buttons/Base/MotionCheck/BoundsCheck.gd"),
|
||||
load(ICON_FOLDER + "BoundsCheck.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"DistanceCheck",
|
||||
"Control",
|
||||
load(CUSTOM_CLASS_FOLDER + "Buttons/Base/MotionCheck/DistanceCheck.gd"),
|
||||
load(ICON_FOLDER + "DistanceCheck.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"MotionCheck",
|
||||
"Control",
|
||||
load(CUSTOM_CLASS_FOLDER + "Buttons/Base/MotionCheck/MotionCheck.gd"),
|
||||
load(ICON_FOLDER + "MotionCheck.svg")
|
||||
)
|
||||
|
||||
# Complex
|
||||
add_custom_type(
|
||||
"ModulateTransitionButton",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "Buttons/Complex/ModulateTransitionButton.gd"),
|
||||
load(ICON_FOLDER + "ModulateTransitionButton.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"StyleTransitionButton",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "Buttons/Complex/StyleTransitionButton.gd"),
|
||||
load(ICON_FOLDER + "StyleTransitionButton.svg")
|
||||
)
|
||||
|
||||
# Carousel
|
||||
add_custom_type(
|
||||
"Carousel",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "Carousel/Carousel.gd"),
|
||||
load(ICON_FOLDER + "Carousel.svg")
|
||||
)
|
||||
|
||||
# CircularContainer
|
||||
add_custom_type(
|
||||
"CircularContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "CircularContainer/CircularContainer.gd"),
|
||||
load(ICON_FOLDER + "CircularContainer.svg")
|
||||
)
|
||||
|
||||
# Drawer
|
||||
add_custom_type(
|
||||
"Drawer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "Drawer/Drawer.gd"),
|
||||
load(ICON_FOLDER + "Drawer.svg")
|
||||
)
|
||||
|
||||
# PaddingContainer
|
||||
add_custom_type(
|
||||
"PaddingContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "PaddingContainer/PaddingContainer.gd"),
|
||||
load(ICON_FOLDER + "PaddingContainer.svg")
|
||||
)
|
||||
|
||||
# ProportionalContainer
|
||||
add_custom_type(
|
||||
"ProportionalContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "ProportionalContainer/ProportionalContainer.gd"),
|
||||
load(ICON_FOLDER + "ProportionalContainer.svg")
|
||||
)
|
||||
|
||||
# Routers
|
||||
add_custom_type(
|
||||
"RouterStack",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "Routers/RouterStack.gd"),
|
||||
load(ICON_FOLDER + "RouterStack.svg")
|
||||
)
|
||||
# Base
|
||||
add_custom_type(
|
||||
"Page",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "Routers/Base/Page.gd"),
|
||||
load(ICON_FOLDER + "Page.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"PageInfo",
|
||||
"Resource",
|
||||
load(CUSTOM_CLASS_FOLDER + "Routers/Base/PageInfo.gd"),
|
||||
load(ICON_FOLDER + "PageInfo.svg")
|
||||
)
|
||||
|
||||
# SizeControllers
|
||||
# MaxSizeContainer
|
||||
add_custom_type(
|
||||
"MaxSizeContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "SizeController/MaxSizeContainer.gd"),
|
||||
load(ICON_FOLDER + "MaxSizeContainer.svg")
|
||||
)
|
||||
# MaxRatioContainer
|
||||
add_custom_type(
|
||||
"MaxRatioContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "SizeController/MaxRatioContainer.gd"),
|
||||
load(ICON_FOLDER + "MaxRatioContainer.svg")
|
||||
)
|
||||
|
||||
# SwapContainer
|
||||
add_custom_type(
|
||||
"SwapContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "SwapContainer/SwapContainer.gd"),
|
||||
load(ICON_FOLDER + "SwapContainer.svg")
|
||||
)
|
||||
|
||||
# TransitionContainers
|
||||
add_custom_type(
|
||||
"ModulateTransitionContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "TransitionContainers/ModulateTransitionContainer.gd"),
|
||||
load(ICON_FOLDER + "ModulateTransitionContainer.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"StyleTransitionContainer",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "TransitionContainers/StyleTransitionContainer.gd"),
|
||||
load(ICON_FOLDER + "StyleTransitionContainer.svg")
|
||||
)
|
||||
add_custom_type(
|
||||
"StyleTransitionPanel",
|
||||
"Container",
|
||||
load(CUSTOM_CLASS_FOLDER + "TransitionContainers/StyleTransitionPanel.gd"),
|
||||
load(ICON_FOLDER + "StyleTransitionPanel.svg")
|
||||
)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
# AnimatableControls
|
||||
# Control
|
||||
remove_custom_type("AnimatableControl")
|
||||
remove_custom_type("AnimatableScrollControl")
|
||||
remove_custom_type("AnimatableZoneControl")
|
||||
remove_custom_type("AnimatableVisibleControl")
|
||||
# Mount
|
||||
remove_custom_type("AnimatableMount")
|
||||
remove_custom_type("AnimatableTransformationMount")
|
||||
|
||||
# Buttons
|
||||
# Base
|
||||
remove_custom_type("AnimatedSwitch")
|
||||
remove_custom_type("HoldButton")
|
||||
# MotionCheck
|
||||
remove_custom_type("BoundsCheck")
|
||||
remove_custom_type("DistanceCheck")
|
||||
remove_custom_type("MotionCheck")
|
||||
|
||||
# Complex
|
||||
remove_custom_type("ModulateTransitionButton")
|
||||
remove_custom_type("StyleTransitionButton")
|
||||
|
||||
# Carousel
|
||||
remove_custom_type("Carousel")
|
||||
|
||||
# CircularContainer
|
||||
remove_custom_type("CircularContainer")
|
||||
|
||||
# Drawer
|
||||
remove_custom_type("Drawer")
|
||||
|
||||
# PaddingContainer
|
||||
remove_custom_type("PaddingContainer")
|
||||
|
||||
# ProportionalContainer
|
||||
remove_custom_type("ProportionalContainer")
|
||||
|
||||
# Routers
|
||||
remove_custom_type("RouterStack")
|
||||
# Base
|
||||
remove_custom_type("Page")
|
||||
remove_custom_type("PageInfo")
|
||||
|
||||
# SizeControllers
|
||||
remove_custom_type("MaxSizeContainer")
|
||||
remove_custom_type("MaxRatioContainer")
|
||||
|
||||
# SwapContainer
|
||||
remove_custom_type("SwapContainer")
|
||||
|
||||
# TransitionContainers
|
||||
remove_custom_type("ModulateTransitionContainer")
|
||||
remove_custom_type("StyleTransitionContainer")
|
||||
remove_custom_type("StyleTransitionPanel")
|
7
godot/addons/FreeControl/plugin.cfg
Normal file
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="FreeControl"
|
||||
description="Multiple Control nodes for easier UI manipulation"
|
||||
author="Xavier Alvarez"
|
||||
version="1.5.1"
|
||||
script="main.gd"
|
|
@ -0,0 +1,139 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name AnimatableControl extends Container
|
||||
## A container to be used for free transformation within a UI.
|
||||
|
||||
## This signal emits when one of the following properties change: scale, position,
|
||||
## rotation, pivot_offset
|
||||
signal transformation_changed
|
||||
|
||||
## The size mode this node's size will be bounded by.
|
||||
enum SIZE_MODE {
|
||||
NONE = 0b00, ## This node's size is not bounded
|
||||
MIN = 0b01, ## This node's size will be greater than or equal to this node's mount size
|
||||
MAX = 0b10, ## This node's size will be less than or equal to this node's mount size
|
||||
EXACT = 0b11 ## This node's size will be the same as this node's mount size
|
||||
}
|
||||
|
||||
## Controls how this node's size is bounded, according to the node's mount size
|
||||
@export var size_mode : SIZE_MODE = SIZE_MODE.EXACT:
|
||||
set(val):
|
||||
if size_mode != val:
|
||||
size_mode = val
|
||||
if _mount:
|
||||
_mount.update_minimum_size()
|
||||
_bound_size()
|
||||
notify_property_list_changed()
|
||||
## Auto sets the pivot to be at some position percentage of the size.
|
||||
@export var pivot_ratio : Vector2:
|
||||
set(val):
|
||||
if pivot_ratio != val:
|
||||
pivot_ratio = val
|
||||
pivot_offset = size * val
|
||||
|
||||
var _mount : AnimatableMount
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
if get_parent() is AnimatableMount:
|
||||
return []
|
||||
return ["This node only serves to be animatable within a UI. Please only attach as a child to a 'AnimatableMount' node."]
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name in ["layout_mode", "anchors_preset"]:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name == "size":
|
||||
if size_mode == SIZE_MODE.EXACT:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if property in ["scale", "position", "rotation"]:
|
||||
transformation_changed.emit()
|
||||
_bound_size()
|
||||
elif property == "pivot_offset":
|
||||
if is_node_ready() && size > Vector2.ZERO && pivot_offset != value:
|
||||
pivot_ratio = pivot_offset / size
|
||||
transformation_changed.emit()
|
||||
return false
|
||||
|
||||
func _init() -> void:
|
||||
resized.connect(_handle_resize)
|
||||
sort_children.connect(_sort_children)
|
||||
tree_exited.connect(_on_tree_exit)
|
||||
tree_entered.connect(_on_tree_enter)
|
||||
item_rect_changed.connect(transformation_changed.emit)
|
||||
func _on_tree_enter() -> void:
|
||||
_mount = (get_parent() as AnimatableMount)
|
||||
if _mount:
|
||||
_mount._on_mount(self)
|
||||
func _on_tree_exit() -> void:
|
||||
if _mount:
|
||||
_mount._on_unmount(self)
|
||||
_mount = null
|
||||
|
||||
|
||||
func _handle_resize() -> void:
|
||||
_bound_size()
|
||||
_update_pivot()
|
||||
func _update_pivot() -> void:
|
||||
set_pivot_offset(pivot_ratio * size)
|
||||
transformation_changed.emit()
|
||||
func _sort_children() -> void:
|
||||
for child : Control in _get_control_children():
|
||||
_resize_child(child)
|
||||
func _resize_child(child : Control) -> void:
|
||||
var child_size := child.get_combined_minimum_size()
|
||||
var set_pos : Vector2
|
||||
|
||||
match child.size_flags_horizontal & ~SIZE_EXPAND:
|
||||
SIZE_FILL:
|
||||
set_pos.x = 0
|
||||
child_size.x = max(child_size.x, size.x)
|
||||
SIZE_SHRINK_BEGIN:
|
||||
set_pos.x = 0
|
||||
SIZE_SHRINK_CENTER:
|
||||
set_pos.x = (size.x - child_size.x) * 0.5
|
||||
SIZE_SHRINK_END:
|
||||
set_pos.x = size.x - child_size.x
|
||||
match child.size_flags_vertical & ~SIZE_EXPAND:
|
||||
SIZE_FILL:
|
||||
set_pos.y = 0
|
||||
child_size.y = max(child_size.y, size.y)
|
||||
SIZE_SHRINK_BEGIN:
|
||||
set_pos.y = 0
|
||||
SIZE_SHRINK_CENTER:
|
||||
set_pos.y = (size.y - child_size.y) * 0.5
|
||||
SIZE_SHRINK_END:
|
||||
set_pos.y = size.y - child_size.y
|
||||
|
||||
fit_child_in_rect(child, Rect2(set_pos, child_size))
|
||||
func _get_minimum_size() -> Vector2:
|
||||
if clip_children: return Vector2.ZERO
|
||||
|
||||
var min_size := Vector2.ZERO
|
||||
for child : Control in _get_control_children():
|
||||
min_size = min_size.max(child.get_combined_minimum_size())
|
||||
return min_size
|
||||
|
||||
func _bound_size() -> void:
|
||||
if !_mount: return
|
||||
|
||||
var new_size : Vector2 = size
|
||||
if size_mode == SIZE_MODE.MAX:
|
||||
new_size = _mount.get_relative_size(self).min(size)
|
||||
elif size_mode == SIZE_MODE.MIN:
|
||||
new_size = _mount.get_relative_size(self).max(size)
|
||||
elif size_mode == SIZE_MODE.EXACT:
|
||||
new_size = _mount.get_relative_size(self)
|
||||
|
||||
if new_size != size:
|
||||
size = new_size
|
||||
|
||||
func _get_control_children() -> Array[Control]:
|
||||
var ret : Array[Control]
|
||||
ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible))
|
||||
return ret
|
||||
|
||||
## Gets the mount this node is currently a child to.[br]
|
||||
## If this node is not a child to any [AnimatableMount] nodes, this returns [code]null[/code] instead.
|
||||
func get_mount() -> AnimatableMount:
|
||||
return _mount
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,72 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name AnimatableScrollControl extends AnimatableControl
|
||||
## A container to be used for free transformation, within a UI, depended on a [ScrollContainer]'s scroll progress.
|
||||
|
||||
## The [ScrollContainer] this node will consider for operations. Is automatically
|
||||
## set to the closet parent [ScrollContainer] in the tree if [member scroll] is
|
||||
## [code]null[/code] and [Engine] is in editor mode.
|
||||
## [br][br]
|
||||
## [b]NOTE[/b]: It is recomended that this node's [AnimatableMount] is a child of
|
||||
## [member scroll].
|
||||
@export var scroll : ScrollContainer:
|
||||
set(val):
|
||||
if scroll != val:
|
||||
if scroll:
|
||||
scroll.get_h_scroll_bar().value_changed.disconnect(_scrolled_horizontal)
|
||||
scroll.get_v_scroll_bar().value_changed.disconnect(_scrolled_vertical)
|
||||
scroll = val
|
||||
if val:
|
||||
val.get_h_scroll_bar().value_changed.connect(_scrolled_horizontal)
|
||||
val.get_v_scroll_bar().value_changed.connect(_scrolled_vertical)
|
||||
|
||||
if is_node_ready():
|
||||
_scrolled_horizontal(val.get_h_scroll_bar().value)
|
||||
_scrolled_vertical(val.get_v_scroll_bar().value)
|
||||
|
||||
func _enter_tree() -> void:
|
||||
if !scroll && Engine.is_editor_hint(): scroll = get_parent_scroll()
|
||||
|
||||
## A virtual function that is called when [member scroll] is horizontally scrolled.
|
||||
## [br][br]
|
||||
## Paramter [param scroll] is the current horizontal progress of the scroll.
|
||||
func _scrolled_horizontal(scroll_hor : float) -> void: pass
|
||||
## A virtual function that is called when [member scroll] is vertically scrolled.
|
||||
## [br][br]
|
||||
## Paramter [param scroll] is the current vertical progress of the scroll.
|
||||
func _scrolled_vertical(scroll_ver : float) -> void: pass
|
||||
|
||||
## Returns the global difference between this node's [AnimatableMount] and
|
||||
## [member scroll] positions.
|
||||
func get_origin_offset() -> Vector2:
|
||||
if !_mount || !scroll: return Vector2.ZERO
|
||||
return _mount.global_position - scroll.global_position
|
||||
## Returns the horizontal and vertical progress of [member scroll].
|
||||
func get_scroll_offset() -> Vector2:
|
||||
if !scroll: return Vector2.ZERO
|
||||
return Vector2(scroll.scroll_horizontal, scroll.scroll_vertical)
|
||||
## Gets the closet parent [ScrollContainer] in the tree.
|
||||
func get_parent_scroll() -> ScrollContainer:
|
||||
var ret : Control = (get_parent() as Control)
|
||||
while ret != null:
|
||||
if ret is ScrollContainer: return ret
|
||||
ret = (ret.get_parent() as Control)
|
||||
return null
|
||||
|
||||
## Returns a percentage of how visible this node's [AnimatableMount] is, within
|
||||
## the rect of [member scroll].
|
||||
func is_visible_percent() -> float:
|
||||
if !_mount || !scroll: return 0
|
||||
return (_mount.get_global_rect().intersection(scroll.get_global_rect()).get_area()) / (_mount.size.x * _mount.size.y)
|
||||
## Returns a percentage of how visible this node's [AnimatableMount] is, within the
|
||||
## horizontal bounds of [member scroll].
|
||||
func get_visible_horizontal_percent() -> float:
|
||||
if !_mount || !scroll: return 0
|
||||
return (min(_mount.global_position.x + _mount.size.x, scroll.global_position.x + scroll.size.x) - max(_mount.global_position.x, scroll.global_position.x)) / _mount.size.x
|
||||
## Returns a percentage of how visible this node's [AnimatableMount] is, within the
|
||||
## vertical bounds of [member scroll].
|
||||
func get_visible_vertical_percent() -> float:
|
||||
if !_mount || !scroll: return 0
|
||||
return (min(_mount.global_position.y + _mount.size.y, scroll.global_position.y + scroll.size.y) - max(_mount.global_position.y, scroll.global_position.y)) / _mount.size.y
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,397 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name AnimatableVisibleControl extends AnimatableScrollControl
|
||||
## A container to be used for free transformation, within a UI, depending on if
|
||||
## the node is visible in a [ScrollContainer] scroll.
|
||||
|
||||
## Emitted when requested threshold has been entered.
|
||||
signal entered_threshold
|
||||
## Emitted when requested threshold has been exited.
|
||||
signal exited_threshold
|
||||
## Emitted when this node's [AnimatableMount]'s rect entered visible range.
|
||||
signal entered_screen
|
||||
## Emitted when this node's [AnimatableMount]'s rect exited visible range.
|
||||
signal exited_screen
|
||||
|
||||
## Modes of threshold type checking.
|
||||
enum CHECK_MODE {
|
||||
NONE = 0b000, ## No behavior.
|
||||
HORIZONTAL = 0b001, ## Only checks horizontally using [member threshold_horizontal].
|
||||
VERTICAL = 0b010, ## Only checks vertically using [member threshold_vertical].
|
||||
BOTH = 0b011 ## Checks horizontally and vertically.
|
||||
}
|
||||
|
||||
## Modes of threshold size.
|
||||
enum THRESHOLD_EDITOR_DIMS {
|
||||
None = 0b00, ## Both horizontal and vertical axis are based on ratio.
|
||||
Horizontal = 0b01, ## Horizontal axis is based on exact pixel and vertical on ratio.
|
||||
Vertical = 0b10, ## Horizontal axis is based on ratio and vertical on exact pixel.
|
||||
Both = 0b11, ## Both horizontal and vertical axis are based on exact pixel.
|
||||
}
|
||||
|
||||
## Color for inner highlighting - Indicates when visiblity is required to met
|
||||
## threshold.
|
||||
const HIGHLIGHT_COLOR := Color(Color.RED, 0.3)
|
||||
## Color for overlap highlighting - Indicates when visiblity is required, starting
|
||||
## from the far end, to met threshold.
|
||||
const ANTI_HIGHLIGHT_COLOR := Color(Color.DARK_CYAN, 1)
|
||||
## Color for helpful lines to make highlighting for clear.
|
||||
const INTERSECT_HIGHLIGHT_COLOR := Color(Color.RED, 0.8)
|
||||
|
||||
@export_group("Mode")
|
||||
## Sets the mode of threshold type checking.
|
||||
@export var check_mode: CHECK_MODE = CHECK_MODE.NONE:
|
||||
set(val):
|
||||
if check_mode != val:
|
||||
check_mode = val
|
||||
notify_property_list_changed()
|
||||
queue_redraw()
|
||||
|
||||
## A flag variable used to distinguish if the threshold amount is described by
|
||||
## a ratio of the size of the [member AnimatableScrollControl.scroll] value, or
|
||||
## by a const pixel value.[br]
|
||||
## Horizontal and vertical axis are consistered differently.
|
||||
## [br][br]
|
||||
## See [enum THRESHOLD_EDITOR_DIMS], [member threshold_horizontal], and [member threshold_vertical].
|
||||
var threshold_pixel : int:
|
||||
set(val):
|
||||
if (threshold_pixel ^ val) & THRESHOLD_EDITOR_DIMS.Horizontal:
|
||||
_scrolled_horizontal(get_scroll_offset().x)
|
||||
if (threshold_pixel ^ val) & THRESHOLD_EDITOR_DIMS.Vertical:
|
||||
_scrolled_vertical(get_scroll_offset().y)
|
||||
threshold_pixel = val
|
||||
notify_property_list_changed()
|
||||
queue_redraw()
|
||||
## The minimum horizontal percentage this node's [AnimatableMount]'s rect must be
|
||||
## visible in [member scroll] for this node to be consistered visible.
|
||||
var threshold_horizontal : float = 0.5:
|
||||
set(val):
|
||||
if threshold_horizontal != val:
|
||||
threshold_horizontal = val
|
||||
_scrolled_horizontal(0)
|
||||
queue_redraw()
|
||||
## The minimum vertical percentage this node's [AnimatableMount]'s rect must be
|
||||
## visible in [member scroll] for this node to be consistered visible.
|
||||
var threshold_vertical : float = 0.5:
|
||||
set(val):
|
||||
if threshold_vertical != val:
|
||||
threshold_vertical = val
|
||||
_scrolled_vertical(0)
|
||||
queue_redraw()
|
||||
## [b]Editor usage only.[/b] Shows or hides the helpful threshold highlighter.
|
||||
var hide_indicator : bool = true:
|
||||
set(val):
|
||||
if hide_indicator != val:
|
||||
hide_indicator = val
|
||||
queue_redraw()
|
||||
|
||||
var _last_threshold_horizontal : float
|
||||
var _last_threshold_vertical : float
|
||||
var _last_visible : bool
|
||||
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
var ret : Array[Dictionary] = []
|
||||
var horizontal : int = 0 if check_mode & CHECK_MODE.HORIZONTAL else PROPERTY_USAGE_READ_ONLY
|
||||
var vertical : int = 0 if check_mode & CHECK_MODE.VERTICAL else PROPERTY_USAGE_READ_ONLY
|
||||
var either : int = horizontal & vertical
|
||||
|
||||
var options : String
|
||||
if !horizontal: options = "Horizontal:1,"
|
||||
if !vertical: options += "Vertical:2"
|
||||
|
||||
ret.append({
|
||||
"name": "Threshold",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_GROUP,
|
||||
"hint_string": ""
|
||||
})
|
||||
ret.append({
|
||||
"name": "threshold_pixel",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_FLAGS,
|
||||
"hint_string": options,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | either
|
||||
})
|
||||
ret.append({
|
||||
"name": "threshold_horizontal",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | horizontal
|
||||
}.merged({} if threshold_pixel & 1 else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001"
|
||||
}))
|
||||
ret.append({
|
||||
"name": "threshold_vertical",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | vertical
|
||||
}.merged({} if threshold_pixel & 2 else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001"
|
||||
}))
|
||||
|
||||
ret.append({
|
||||
"name": "Indicator",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_GROUP,
|
||||
"hint_str": ""
|
||||
})
|
||||
ret.append({
|
||||
"name": "hide_indicator",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
return ret
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
if property == "threshold_pixel":
|
||||
if self[property] != 0: return true
|
||||
elif property in ["threshold_horizontal", "threshold_vertical"]:
|
||||
if self[property] != 0.5: return true
|
||||
elif property == "hide_indicator":
|
||||
return !hide_indicator
|
||||
return false
|
||||
func _property_get_revert(property: StringName) -> Variant:
|
||||
if property == "threshold_pixel":
|
||||
return 0
|
||||
elif property in ["threshold_horizontal", "threshold_vertical"]:
|
||||
return 0.5
|
||||
elif property == "hide_indicator":
|
||||
return true
|
||||
return null
|
||||
|
||||
func _init() -> void:
|
||||
item_rect_changed.connect(queue_redraw)
|
||||
|
||||
func _get_threshold_size() -> Array[Vector2]:
|
||||
var ratio_thr : Vector2
|
||||
var full_thr : Vector2
|
||||
|
||||
if is_zero_approx(_mount.size.x):
|
||||
ratio_thr.x = 1
|
||||
full_thr.x = _mount.size.x
|
||||
elif threshold_pixel & THRESHOLD_EDITOR_DIMS.Horizontal:
|
||||
var hor := clamp(threshold_horizontal, 0, _mount.size.x)
|
||||
ratio_thr.x = hor / _mount.size.x
|
||||
full_thr.x = hor
|
||||
else:
|
||||
var hor := clamp(threshold_horizontal, 0, 1)
|
||||
ratio_thr.x = hor
|
||||
full_thr.x = hor * _mount.size.x
|
||||
|
||||
if is_zero_approx(_mount.size.y):
|
||||
ratio_thr.y = 1
|
||||
full_thr.y = _mount.size.y
|
||||
elif threshold_pixel & THRESHOLD_EDITOR_DIMS.Vertical:
|
||||
var vec := clamp(threshold_vertical, 0, _mount.size.y)
|
||||
ratio_thr.y = vec / _mount.size.y
|
||||
full_thr.y = vec
|
||||
else:
|
||||
var vec := clamp(threshold_vertical, 0, 1)
|
||||
ratio_thr.y = vec
|
||||
full_thr.y = vec * _mount.size.y
|
||||
|
||||
return [ratio_thr, full_thr]
|
||||
func _draw() -> void:
|
||||
if !_mount || !Engine.is_editor_hint() || hide_indicator: return
|
||||
|
||||
var threshold_adjust := _get_threshold_size()
|
||||
|
||||
draw_set_transform(-position)
|
||||
draw_rect(Rect2(Vector2.ZERO, size), Color.CORAL, false)
|
||||
|
||||
match check_mode:
|
||||
CHECK_MODE.HORIZONTAL:
|
||||
var left := threshold_adjust[1].x
|
||||
var right := size.x - left
|
||||
|
||||
if threshold_adjust[0].x > 0.5:
|
||||
left = size.x - left
|
||||
right = size.x - right
|
||||
|
||||
_draw_highlight(
|
||||
left,
|
||||
0,
|
||||
right,
|
||||
size.y,
|
||||
threshold_adjust[0].x < 0.5
|
||||
)
|
||||
CHECK_MODE.VERTICAL:
|
||||
var top := threshold_adjust[1].y
|
||||
var bottom := size.y - top
|
||||
|
||||
if threshold_adjust[0].y > 0.5:
|
||||
top = size.y - top
|
||||
bottom = size.y - bottom
|
||||
|
||||
_draw_highlight(
|
||||
0,
|
||||
top,
|
||||
size.x,
|
||||
bottom,
|
||||
threshold_adjust[0].y < 0.5
|
||||
)
|
||||
CHECK_MODE.BOTH:
|
||||
var left := threshold_adjust[1].x
|
||||
var right := size.x - left
|
||||
var top := threshold_adjust[1].y
|
||||
var bottom := size.y - top
|
||||
|
||||
var draw_middle : bool = true
|
||||
if threshold_adjust[0].x >= 0.5:
|
||||
left = size.x - left
|
||||
right = size.x - right
|
||||
draw_middle = false
|
||||
if threshold_adjust[0].y >= 0.5:
|
||||
top = size.y - top
|
||||
bottom = size.y - bottom
|
||||
draw_middle = false
|
||||
|
||||
_draw_highlight(
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
draw_middle
|
||||
)
|
||||
|
||||
if !draw_middle:
|
||||
if threshold_adjust[0].x >= 0.5:
|
||||
if threshold_adjust[0].y < 0.5:
|
||||
draw_line(
|
||||
Vector2(left, top),
|
||||
Vector2(right, top),
|
||||
INTERSECT_HIGHLIGHT_COLOR,
|
||||
5
|
||||
)
|
||||
draw_line(
|
||||
Vector2(left, bottom),
|
||||
Vector2(right, bottom),
|
||||
INTERSECT_HIGHLIGHT_COLOR,
|
||||
5
|
||||
)
|
||||
elif threshold_adjust[0].y >= 0.5:
|
||||
draw_line(
|
||||
Vector2(left, top),
|
||||
Vector2(left, bottom),
|
||||
INTERSECT_HIGHLIGHT_COLOR,
|
||||
5
|
||||
)
|
||||
draw_line(
|
||||
Vector2(right, top),
|
||||
Vector2(right, bottom),
|
||||
INTERSECT_HIGHLIGHT_COLOR,
|
||||
5
|
||||
)
|
||||
func _draw_highlight(
|
||||
left : float,
|
||||
top : float,
|
||||
right : float,
|
||||
bottom : float,
|
||||
draw_middle : bool
|
||||
) -> void:
|
||||
# Middle
|
||||
if draw_middle:
|
||||
draw_rect(Rect2(Vector2(left, top), Vector2(right - left, bottom - top)), HIGHLIGHT_COLOR)
|
||||
return
|
||||
# Outer
|
||||
# Left
|
||||
draw_rect(Rect2(Vector2(0, 0), Vector2(left, size.y)), ANTI_HIGHLIGHT_COLOR)
|
||||
# Right
|
||||
draw_rect(Rect2(Vector2(right, 0), Vector2(size.x - right, size.y)), ANTI_HIGHLIGHT_COLOR)
|
||||
# Top
|
||||
draw_rect(Rect2(Vector2(left, 0), Vector2(right - left, top)), ANTI_HIGHLIGHT_COLOR)
|
||||
# Bottom
|
||||
draw_rect(Rect2(Vector2(left, bottom), Vector2(right - left, size.y - bottom)), ANTI_HIGHLIGHT_COLOR)
|
||||
|
||||
func _scrolled_horizontal(_scroll_hor : float) -> void:
|
||||
if !(check_mode & CHECK_MODE.HORIZONTAL): return
|
||||
|
||||
var threshold_adjust := _get_threshold_size()
|
||||
var val : float = is_visible_percent()
|
||||
|
||||
# Checks if visible
|
||||
if val > 0:
|
||||
# If visible, but wasn't visible last scroll, then it entered visible area
|
||||
if !_last_visible:
|
||||
entered_screen.emit()
|
||||
_last_visible = true
|
||||
# Calls the while function
|
||||
_while_visible(val)
|
||||
# Else, if visible last frame, then it exited visible area
|
||||
elif _last_visible:
|
||||
_while_visible(0)
|
||||
exited_screen.emit()
|
||||
_last_visible = false
|
||||
|
||||
val = get_visible_horizontal_percent()
|
||||
# Checks if in threshold
|
||||
if val >= threshold_adjust[0].x:
|
||||
# If in threshold, but not last frame, then it entered threshold area
|
||||
if _last_threshold_horizontal < threshold_adjust[0].x:
|
||||
entered_threshold.emit()
|
||||
# Calls the while function
|
||||
_while_threshold(val)
|
||||
# If in threshold, but not last frame, then it entered threshold area
|
||||
elif _last_threshold_horizontal > threshold_adjust[0].x:
|
||||
_while_threshold(0)
|
||||
exited_threshold.emit()
|
||||
_last_threshold_horizontal = val
|
||||
func _scrolled_vertical(_scroll_ver : float) -> void:
|
||||
if !(check_mode & CHECK_MODE.VERTICAL): return
|
||||
|
||||
var threshold_adjust := _get_threshold_size()
|
||||
var val : float = is_visible_percent()
|
||||
|
||||
# Checks if visible
|
||||
if val > 0:
|
||||
# If visible, but wasn't visible last scroll, then it entered visible area
|
||||
if !_last_visible:
|
||||
entered_screen.emit()
|
||||
_last_visible = true
|
||||
# Calls the while function
|
||||
_while_visible(val)
|
||||
# Else, if visible last frame, then it exited visible area
|
||||
elif _last_visible:
|
||||
_while_visible(0)
|
||||
exited_screen.emit()
|
||||
_last_visible = false
|
||||
|
||||
val = get_visible_vertical_percent()
|
||||
# Checks if in threshold
|
||||
if val >= threshold_adjust[0].y:
|
||||
# If in threshold, but not last frame, then it entered threshold area
|
||||
if _last_threshold_vertical < threshold_adjust[0].y:
|
||||
entered_threshold.emit()
|
||||
# Calls the while function
|
||||
_while_threshold(val)
|
||||
# If in threshold, but not last frame, then it entered threshold area
|
||||
elif _last_threshold_vertical > threshold_adjust[0].y:
|
||||
_while_threshold(0)
|
||||
exited_threshold.emit()
|
||||
_last_threshold_vertical = val
|
||||
|
||||
|
||||
|
||||
# Public Functions
|
||||
|
||||
## Returns the rect [threshold_horizontal] and [threshold_vertical] create.
|
||||
func get_threshold_rect(consider_mode : bool = false) -> Rect2:
|
||||
var threshold_adjust := _get_threshold_size()
|
||||
return Rect2(threshold_adjust[1], size - threshold_adjust[1])
|
||||
|
||||
|
||||
|
||||
# Virtual Functions
|
||||
|
||||
## A virtual function that is called while this node is in the visible area of it's
|
||||
## scroll. Is called after each scroll of [member scroll].
|
||||
## [br][br]
|
||||
## Paramter [param intersect] is the current visible percent.
|
||||
func _while_visible(intersect : float) -> void: pass
|
||||
## A virtual function that is called while this node's visible threshold is met. Is
|
||||
## called after each scroll of [member scroll].
|
||||
## [br][br]
|
||||
## Paramter [param intersect] is the current threshold value met.
|
||||
func _while_threshold(intersect : float) -> void: pass
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,386 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name AnimatableZoneControl extends AnimatableScrollControl
|
||||
## A container to be used for free transformation, within a UI, depended on a
|
||||
## [ScrollContainer]'s scroll progress.
|
||||
|
||||
## Modes of zone type checking.
|
||||
enum CHECK_MODE {
|
||||
NONE = 0b000, ## No behavior.
|
||||
HORIZONTAL = 0b001, ## Only checks if this node's mount is in the zone horizontally.
|
||||
VERTICAL = 0b010, ## Only checks if this node's mount is in the zone vertically.
|
||||
BOTH = 0b011 ## Checks horizontally and vertically.
|
||||
}
|
||||
|
||||
## Modes of zone size and center position.
|
||||
enum ZONE_EDITOR_DIMS {
|
||||
None = 0b00, ## Both horizontal and vertical axis are based on ratio.
|
||||
Horizontal = 0b01, ## Horizontal axis is based on exact pixel and vertical on ratio.
|
||||
Vertical = 0b10, ## Horizontal axis is based on ratio and vertical on exact pixel.
|
||||
Both = 0b11, ## Both horizontal and vertical axis are based on exact pixel.
|
||||
}
|
||||
|
||||
## Emitted when this node's [AnimatableMount]'s entered the zone area.
|
||||
signal entered_zone
|
||||
## Emitted when this node's [AnimatableMount]'s exited the zone area.
|
||||
signal exited_zone
|
||||
|
||||
## Color for inner highlighting - Indicates when visiblity is required to met threshold.
|
||||
const HIGHLIGHT_COLOR := Color(Color.RED, 0.3)
|
||||
|
||||
@export_group("Mode")
|
||||
## Sets the mode of zone checking.
|
||||
@export var check_mode: CHECK_MODE = CHECK_MODE.NONE:
|
||||
set(val):
|
||||
if check_mode != val:
|
||||
check_mode = val
|
||||
notify_property_list_changed()
|
||||
queue_redraw()
|
||||
|
||||
## A flag variable used to distinguish if the center of the zone is described by
|
||||
## a ratio of the size of the [member AnimatableScrollControl.scroll] value, or
|
||||
## by a const pixel value.[br]
|
||||
## Horizontal and vertical axis are consistered differently.
|
||||
## [br][br]
|
||||
## See [enum ZONE_EDITOR_DIMS], [member zone_horizontal], and [member zone_vertical].
|
||||
var zone_point_pixel : int = 0:
|
||||
set(val):
|
||||
if (zone_point_pixel ^ val) & ZONE_EDITOR_DIMS.Horizontal:
|
||||
_scrolled_horizontal(get_scroll_offset().x)
|
||||
if (zone_point_pixel ^ val) & ZONE_EDITOR_DIMS.Vertical:
|
||||
_scrolled_vertical(get_scroll_offset().y)
|
||||
zone_point_pixel = val
|
||||
notify_property_list_changed()
|
||||
queue_redraw()
|
||||
var _zone_horizontal : float = 0.5
|
||||
## The horizontal position of the zone's center, described either as the ratio of
|
||||
## the size of the [member AnimatableScrollControl.scroll] value, or by a const
|
||||
## pixel value.[br]
|
||||
## [br][br]
|
||||
## See [member zone_point_pixel]
|
||||
var zone_horizontal : float:
|
||||
get: return _zone_horizontal
|
||||
set(val):
|
||||
if _zone_horizontal != val:
|
||||
_zone_horizontal = val
|
||||
_scrolled_horizontal(get_scroll_offset().x)
|
||||
queue_redraw()
|
||||
var _zone_vertical : float = 0.5
|
||||
## The vertical position of the zone's center, described either as the ratio of
|
||||
## the size of the [member AnimatableScrollControl.scroll] value, or by a const
|
||||
## pixel value.[br]
|
||||
## [br][br]
|
||||
## See [member zone_point_pixel]
|
||||
var zone_vertical : float = 0.5:
|
||||
get: return _zone_vertical
|
||||
set(val):
|
||||
if _zone_vertical != val:
|
||||
_zone_vertical = val
|
||||
_scrolled_vertical(get_scroll_offset().y)
|
||||
queue_redraw()
|
||||
|
||||
## A flag variable used to distinguish if the size of the zone is described by
|
||||
## a ratio of the size of the [member AnimatableScrollControl.scroll] value, or
|
||||
## by a const pixel value.[br]
|
||||
## Horizontal and vertical axis are consistered differently.
|
||||
## [br][br]
|
||||
## See [enum ZONE_EDITOR_DIMS], [member zone_horizontal], and [member zone_vertical].
|
||||
var zone_range_by_pixel : int = 0:
|
||||
set(val):
|
||||
if (zone_point_pixel ^ val) & ZONE_EDITOR_DIMS.Horizontal:
|
||||
_scrolled_horizontal(get_scroll_offset().x)
|
||||
if (zone_point_pixel ^ val) & ZONE_EDITOR_DIMS.Vertical:
|
||||
_scrolled_horizontal(get_scroll_offset().y)
|
||||
zone_range_by_pixel = val
|
||||
notify_property_list_changed()
|
||||
queue_redraw()
|
||||
var _zone_range_horizontal : float = 0.05
|
||||
## The horizontal size of the zone, described either as the ratio of the size
|
||||
## of the [member AnimatableScrollControl.scroll] value, or by a const pixel
|
||||
## value.[br]
|
||||
## [br][br]
|
||||
## See [member zone_vertical]
|
||||
var zone_range_horizontal : float:
|
||||
get: return _zone_range_horizontal
|
||||
set(val):
|
||||
if _zone_range_horizontal != val:
|
||||
_zone_range_horizontal = val
|
||||
_scrolled_horizontal(get_scroll_offset().x)
|
||||
queue_redraw()
|
||||
var _zone_range_vertical : float = 0.05
|
||||
## The vertical size of the zone, described either as the ratio of the size
|
||||
## of the [member AnimatableScrollControl.scroll] value, or by a const pixel
|
||||
## value.[br]
|
||||
## [br][br]
|
||||
## See [member zone_vertical]
|
||||
var zone_range_vertical : float:
|
||||
get: return _zone_range_vertical
|
||||
set(val):
|
||||
if _zone_range_vertical != val:
|
||||
_zone_range_vertical = val
|
||||
_scrolled_vertical(get_scroll_offset().y)
|
||||
queue_redraw()
|
||||
|
||||
## [b]Editor usage only.[/b] Shows or hides the helpful threshold highlighter.
|
||||
var hide_indicator : bool = true:
|
||||
set(val):
|
||||
if hide_indicator != val:
|
||||
hide_indicator = val
|
||||
queue_redraw()
|
||||
|
||||
var _last_overlapped : int = 2
|
||||
|
||||
func _draw() -> void:
|
||||
if !_mount || !Engine.is_editor_hint() || hide_indicator || !scroll || check_mode == CHECK_MODE.NONE: return
|
||||
|
||||
var draw_rect := get_zone_rect()
|
||||
var scroll_transform := scroll.get_global_transform()
|
||||
var transform := _mount.get_global_transform()
|
||||
|
||||
draw_set_transform(scroll_transform.get_origin() - transform.get_origin(),
|
||||
scroll_transform.get_rotation() - transform.get_rotation(),
|
||||
scroll_transform.get_scale() / transform.get_scale())
|
||||
draw_rect(draw_rect, HIGHLIGHT_COLOR)
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
var ret : Array[Dictionary] = []
|
||||
var horizontal : int = 0 if check_mode & CHECK_MODE.HORIZONTAL else PROPERTY_USAGE_READ_ONLY
|
||||
var vertical : int = 0 if check_mode & CHECK_MODE.VERTICAL else PROPERTY_USAGE_READ_ONLY
|
||||
var either : int = horizontal & vertical
|
||||
|
||||
var options : String
|
||||
if !horizontal: options = "Horizontal:1,"
|
||||
if !vertical: options += "Vertical:2"
|
||||
|
||||
ret.append({
|
||||
"name": "Zone Point",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_GROUP,
|
||||
"hint_string": ""
|
||||
})
|
||||
ret.append({
|
||||
"name": "zone_point_pixel",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_FLAGS,
|
||||
"hint_string": options,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | either
|
||||
})
|
||||
ret.append({
|
||||
"name": "zone_horizontal",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | horizontal
|
||||
}.merged({} if zone_point_pixel & 1 else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_less,or_greater"
|
||||
}))
|
||||
ret.append({
|
||||
"name": "zone_vertical",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | vertical
|
||||
}.merged({} if zone_point_pixel & 2 else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_less,or_greater"
|
||||
}))
|
||||
|
||||
ret.append({
|
||||
"name": "Zone Range",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_GROUP,
|
||||
"hint_string": ""
|
||||
})
|
||||
ret.append({
|
||||
"name": "zone_range_by_pixel",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_FLAGS,
|
||||
"hint_string": options,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | either
|
||||
})
|
||||
ret.append({
|
||||
"name": "zone_range_horizontal",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | horizontal
|
||||
}.merged({} if zone_range_by_pixel & 1 else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_less,or_greater"
|
||||
}))
|
||||
ret.append({
|
||||
"name": "zone_range_vertical",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | vertical
|
||||
}.merged({} if zone_range_by_pixel & 2 else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_less,or_greater"
|
||||
}))
|
||||
|
||||
ret.append({
|
||||
"name": "Indicator",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_GROUP,
|
||||
"hint_str": ""
|
||||
})
|
||||
ret.append({
|
||||
"name": "hide_indicator",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
return ret
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
if property in ["zone_point_pixel", "zone_range_by_pixel"]:
|
||||
if self[property] != 0: return true
|
||||
elif property in ["zone_horizontal", "zone_vertical"]:
|
||||
if self[property] != 0.5: return true
|
||||
elif property in ["zone_range_horizontal", "zone_range_vertical"]:
|
||||
if self[property] != 0.05: return true
|
||||
elif property == "hide_indicator":
|
||||
return !hide_indicator
|
||||
return false
|
||||
func _property_get_revert(property: StringName) -> Variant:
|
||||
if property in ["zone_point_pixel", "zone_range_by_pixel"]:
|
||||
return 0
|
||||
elif property in ["zone_horizontal", "zone_vertical"]:
|
||||
return 0.5
|
||||
elif property in ["zone_range_horizontal", "zone_range_vertical"]:
|
||||
return 0.05
|
||||
elif property == "hide_indicator":
|
||||
return true
|
||||
return null
|
||||
|
||||
func _scrolled_horizontal(scroll_hor : float) -> void:
|
||||
if !(check_mode & CHECK_MODE.HORIZONTAL) || !scroll: return
|
||||
|
||||
var overlapped := is_overlaped_with_activate_zone()
|
||||
if overlapped:
|
||||
if _last_overlapped != 1:
|
||||
entered_zone.emit()
|
||||
_last_overlapped = 1
|
||||
_while_in_zone(zone_local_scroll().x)
|
||||
elif _last_overlapped:
|
||||
_last_overlapped = 0
|
||||
_while_in_zone(1 if zone_local_scroll().x > 0.5 else 0)
|
||||
exited_zone.emit()
|
||||
func _scrolled_vertical(scroll_ver : float) -> void:
|
||||
if !(check_mode & CHECK_MODE.VERTICAL) || !scroll: return
|
||||
|
||||
var overlapped := is_overlaped_with_activate_zone()
|
||||
if overlapped:
|
||||
if _last_overlapped != 1:
|
||||
entered_zone.emit()
|
||||
_last_overlapped = 1
|
||||
_while_in_zone(zone_local_scroll().y)
|
||||
elif _last_overlapped:
|
||||
_last_overlapped = 0
|
||||
_while_in_zone(1 if zone_local_scroll().y > 0.5 else 0)
|
||||
exited_zone.emit()
|
||||
|
||||
func _get_zone_pos() -> Vector2:
|
||||
var ret := Vector2(zone_horizontal, zone_vertical)
|
||||
if zone_point_pixel == ZONE_EDITOR_DIMS.None:
|
||||
ret *= scroll.size
|
||||
elif zone_point_pixel == ZONE_EDITOR_DIMS.Vertical:
|
||||
ret.x *= scroll.size.x
|
||||
elif zone_point_pixel == ZONE_EDITOR_DIMS.Horizontal:
|
||||
ret.y *= scroll.size.y
|
||||
return ret
|
||||
func _get_zone_range() -> Vector2:
|
||||
var ret := Vector2(zone_range_horizontal, zone_range_vertical) * 0.5
|
||||
if zone_range_by_pixel == ZONE_EDITOR_DIMS.None:
|
||||
ret *= scroll.size
|
||||
elif zone_range_by_pixel == ZONE_EDITOR_DIMS.Vertical:
|
||||
ret.x *= scroll.size.x
|
||||
elif zone_range_by_pixel == ZONE_EDITOR_DIMS.Horizontal:
|
||||
ret.y *= scroll.size.y
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
# Public Functions
|
||||
|
||||
## Returns [code]true[/code] if this node's mount is overlaping the zone area.[br]
|
||||
## This function's value is dependant on the value of [member check_mode].
|
||||
func is_overlaped_with_activate_zone() -> bool:
|
||||
var item_pos_start := get_origin_offset()
|
||||
var item_pos_end := item_pos_start + size
|
||||
|
||||
var zone_pos := _get_zone_pos()
|
||||
var zone_range := _get_zone_range()
|
||||
var zone_pos_start := zone_pos - zone_range
|
||||
var zone_pos_end := zone_pos + zone_range
|
||||
|
||||
if (check_mode == CHECK_MODE.VERTICAL):
|
||||
return (zone_pos_start.y <= item_pos_end.y && zone_pos_end.y >= item_pos_start.y)
|
||||
elif (check_mode == CHECK_MODE.HORIZONTAL):
|
||||
return (zone_pos_start.x <= item_pos_end.x && zone_pos_end.x >= item_pos_start.x)
|
||||
elif (check_mode == CHECK_MODE.BOTH):
|
||||
return (zone_pos_start.y <= item_pos_end.y && zone_pos_end.y >= item_pos_start.y) && (zone_pos_start.x <= item_pos_end.x && zone_pos_end.x >= item_pos_start.x)
|
||||
return false
|
||||
|
||||
## Gets the Rect2 associated to the zone.
|
||||
## [br][br]
|
||||
## Also see [method get_zone_global_rect], [member zone_horizontal],
|
||||
## [member zone_vertical], [member zone_range_horizontal], [member zone_range_vertical].
|
||||
func get_zone_rect() -> Rect2:
|
||||
if check_mode == CHECK_MODE.NONE || !scroll: return Rect2()
|
||||
|
||||
var ret : Rect2 = scroll.get_rect()
|
||||
var zone_pos := _get_zone_pos()
|
||||
var zone_range := _get_zone_range()
|
||||
|
||||
if (check_mode == CHECK_MODE.VERTICAL):
|
||||
var pos := zone_pos.y - zone_range.y
|
||||
var max_pos := max(pos, 0)
|
||||
|
||||
ret.position.y = max_pos
|
||||
ret.size.y = min(zone_range.y + zone_range.y + pos, scroll.size.y) - max_pos
|
||||
elif (check_mode == CHECK_MODE.HORIZONTAL):
|
||||
var pos := zone_pos.x - zone_range.x
|
||||
var max_pos := max(pos, 0)
|
||||
|
||||
ret.position.x = max_pos
|
||||
ret.size.x = min(zone_range.x + zone_range.x + pos, scroll.size.x) - max_pos
|
||||
elif (check_mode == CHECK_MODE.BOTH):
|
||||
var pos := zone_pos - zone_range
|
||||
var max_pos := pos.max(Vector2.ZERO)
|
||||
|
||||
ret.position = max_pos
|
||||
ret.size = scroll.size.min(zone_range + zone_range + pos) - max_pos
|
||||
return ret
|
||||
## Gets the global Rect2 associated to the zone.
|
||||
## [br][br]
|
||||
## Also see [method get_zone_rect], [member zone_horizontal], [member zone_vertical],
|
||||
## [member zone_range_horizontal], [member zone_range_vertical].
|
||||
func get_zone_global_rect() -> Rect2:
|
||||
if !scroll: return Rect2()
|
||||
|
||||
var zone_rect := get_zone_rect()
|
||||
zone_rect.position += scroll.global_position
|
||||
return zone_rect
|
||||
## Gets the percentage of this node's mount intersection with the zone.
|
||||
## [br][br]
|
||||
## Also see [method get_zone_rect], [method get_zone_global_rect].
|
||||
func in_zone_percent() -> float:
|
||||
if !_mount: return 0
|
||||
return (_mount.get_global_rect().intersection(get_zone_global_rect()).get_area()) / (_mount.size.x * _mount.size.y)
|
||||
## The local scroll within the zone zone. Returns [code]0[/code] if this node's
|
||||
## mount is not inside the zone area.
|
||||
## [br][br]
|
||||
## Also see [method get_zone_rect], [method get_zone_global_rect].
|
||||
func zone_local_scroll() -> Vector2:
|
||||
if !_mount: return Vector2.ZERO
|
||||
|
||||
var zone := get_zone_global_rect()
|
||||
var mount := _mount.get_global_rect()
|
||||
|
||||
if zone.size + mount.size == Vector2.ZERO: return Vector2.ZERO
|
||||
return Vector2.ONE + ((zone.position - _mount.global_position - mount.size) / (zone.size + mount.size)).clampf(-1, 0)
|
||||
|
||||
|
||||
|
||||
# Virtual Functions
|
||||
|
||||
## A virtual function that is called while this node is in the zone area. Is called
|
||||
## after each scroll of [member scroll].
|
||||
## [br][br]
|
||||
## Paramter [param _scroll] is the local scroll within the zone.
|
||||
func _while_in_zone(scroll : float) -> void: pass
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,50 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name AnimatableMount extends Control
|
||||
## Used as a mount for size consistency between children [AnimatableControl] nodes.
|
||||
|
||||
## Emits before children are sorted
|
||||
signal pre_sort_children
|
||||
## Emits after children have been sorted
|
||||
signal sort_children
|
||||
|
||||
var _min_size : Vector2
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
for child : Node in get_children():
|
||||
if child is AnimatableControl: return []
|
||||
return ["This node has no 'AnimatableControl' nodes as children"]
|
||||
func _get_minimum_size() -> Vector2:
|
||||
if clip_children: return Vector2.ZERO
|
||||
_update_children_minimum_size()
|
||||
return _min_size
|
||||
func _update_children_minimum_size() -> void:
|
||||
_min_size = Vector2.ZERO
|
||||
|
||||
# Ensures size is the same as the largest size (of both axis) of children
|
||||
for child : Node in get_children():
|
||||
if child is AnimatableControl:
|
||||
if child.size_mode & child.SIZE_MODE.MIN:
|
||||
_min_size = _min_size.max(child.get_combined_minimum_size())
|
||||
|
||||
func _init() -> void:
|
||||
resized.connect(_sort_children, CONNECT_DEFERRED)
|
||||
size_flags_changed.connect(_sort_children, CONNECT_DEFERRED)
|
||||
func _sort_children() -> void:
|
||||
pre_sort_children.emit()
|
||||
for child : Node in get_children():
|
||||
if child is AnimatableControl:
|
||||
child._bound_size()
|
||||
sort_children.emit()
|
||||
|
||||
## A virtual helper function that should be used when creating your own mounts.[br]
|
||||
## Is called upon an [AnimatableControl] being added as a child.
|
||||
func _on_mount(control : AnimatableControl) -> void: pass
|
||||
## A virtual helper function that should be used when creating your own mounts.[br]
|
||||
## Is called upon an [AnimatableControl] being removed as a child.
|
||||
func _on_unmount(control : AnimatableControl) -> void: pass
|
||||
## A helper function that should be used when creating your own mounts.[br]
|
||||
## Returns size of this mount.
|
||||
func get_relative_size(control : AnimatableControl) -> Vector2: return size
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,138 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name AnimatableTransformationMount extends AnimatableMount
|
||||
## An [AnimatableMount] that adjusts for it's children 2D transformations: Rotation, Position, and Scale.
|
||||
|
||||
## If [code]true[/code] this node will adjust it's size to fit its children's scales.
|
||||
@export var adjust_scale : bool:
|
||||
set(val):
|
||||
if val != adjust_scale:
|
||||
adjust_scale = val
|
||||
update_minimum_size()
|
||||
## If [code]true[/code] this node will adjust it's size to fit its children's rotations.[br]
|
||||
## [b]NOTE[/b]: A large [member pivot_offset] can cause floating point precision issues.
|
||||
@export var adjust_rotate : bool:
|
||||
set(val):
|
||||
if val != adjust_rotate:
|
||||
adjust_rotate = val
|
||||
update_minimum_size()
|
||||
## If [code]true[/code] this node adjust its children's positions inside it's size.
|
||||
@export var adjust_position : bool:
|
||||
set(val):
|
||||
if val != adjust_position:
|
||||
adjust_position = val
|
||||
update_minimum_size()
|
||||
|
||||
var _child_min_size : Vector2
|
||||
|
||||
func _update_children_minimum_size() -> void:
|
||||
var _old_min_size := _min_size
|
||||
_min_size = Vector2.ZERO
|
||||
_child_min_size = Vector2.ZERO
|
||||
|
||||
var children_info: Array[Array] = []
|
||||
|
||||
for child : AnimatableControl in get_children():
|
||||
if child:
|
||||
var child_size : Vector2
|
||||
var child_offset : Vector2
|
||||
|
||||
# Scale child size, if needed
|
||||
if adjust_scale:
|
||||
child_size = child.get_combined_minimum_size() * child.scale
|
||||
else:
|
||||
child_size = child.get_combined_minimum_size()
|
||||
_child_min_size = _child_min_size.max(child_size)
|
||||
|
||||
# Rotates child size, if needed.
|
||||
if adjust_rotate:
|
||||
child_offset = child.pivot_offset
|
||||
if adjust_scale: child_offset *= child.scale
|
||||
|
||||
# Gets the bounding box of the rect, when rotated around a pivot
|
||||
var bb_rect := _get_rotated_rect_bounding_box(
|
||||
Rect2(Vector2.ZERO, child_size),
|
||||
child_offset,
|
||||
child.rotation
|
||||
)
|
||||
|
||||
child_size = bb_rect.size
|
||||
child_offset = bb_rect.position
|
||||
|
||||
children_info.append([child, child_size, child_offset])
|
||||
_min_size = _min_size.max(child_size)
|
||||
|
||||
# Leaves position adjusts after so size, after calculating min-size of children, can be used
|
||||
if adjust_position:
|
||||
if _old_min_size != _min_size:
|
||||
# If in Container, and min_size changed, update after the container as resorted children
|
||||
if get_parent_control() is Container:
|
||||
get_parent_control().sort_children.connect(_adjust_children_positions.bind(children_info), CONNECT_ONE_SHOT)
|
||||
else:
|
||||
# Otherwise, if min_size changed still, update after minimum_size_changed changed
|
||||
minimum_size_changed.connect(_adjust_children_positions.bind(children_info), CONNECT_ONE_SHOT | CONNECT_DEFERRED)
|
||||
update_minimum_size()
|
||||
else:
|
||||
# If min_size did not change, deffer children position changes
|
||||
call_deferred("_adjust_children_positions", children_info)
|
||||
elif _old_min_size != _min_size:
|
||||
update_minimum_size()
|
||||
func _adjust_children_positions(children_info: Array[Array]) -> void:
|
||||
for child_info : Array in children_info:
|
||||
var child : AnimatableControl = child_info[0]
|
||||
var child_size : Vector2 = child_info[1]
|
||||
var child_offset : Vector2 = child_info[2]
|
||||
|
||||
var piv_offset : Vector2
|
||||
# Rotates pivot, if needed
|
||||
if adjust_rotate:
|
||||
piv_offset = -child.pivot_offset.rotated(rotation)
|
||||
else:
|
||||
piv_offset = -child.pivot_offset
|
||||
# If adjusts the pivot by scale, if needed
|
||||
if adjust_scale:
|
||||
piv_offset *= (child.scale - Vector2.ONE)
|
||||
|
||||
# Not clamp, because min should have priorty
|
||||
# max_size_adjusted_for_child_size = size - child_size - child_offset - piv_offset
|
||||
# min_size_adjusted_for_child_size = -piv_offset - child_offset
|
||||
var new_pos := child.position.min(size - child_size - child_offset - piv_offset).max(-piv_offset - child_offset)
|
||||
|
||||
# Changes position, if needed
|
||||
if child.position != new_pos: child.position = new_pos
|
||||
|
||||
func _get_rotated_rect_bounding_box(rect : Rect2, pivot : Vector2, angle : float) -> Rect2:
|
||||
# Base Values
|
||||
var pos := rect.position
|
||||
var sze := rect.size
|
||||
var trig := Vector2(cos(angle), sin(angle))
|
||||
|
||||
# Simplified equation for centerPoint - bb_size*0.5
|
||||
var bb_pos := Vector2(
|
||||
(sze.x * (trig.x - abs(trig.x)) - sze.y * (trig.y + abs(trig.y))) * 0.5 + pivot.x * (1 - trig.x) + trig.y * pivot.y + pos.x,
|
||||
(sze.x * (trig.y - abs(trig.y)) + sze.y * (trig.x - abs(trig.x))) * 0.5 + pivot.y * (1 - trig.x) - trig.y * pivot.x + pos.y
|
||||
)
|
||||
trig = trig.abs()
|
||||
## Finds the fix of the bounding box of the rotated rectangle
|
||||
var bb_size := Vector2(
|
||||
sze.x * trig.x + sze.y * trig.y,
|
||||
sze.x * trig.y + sze.y * trig.x
|
||||
)
|
||||
|
||||
return Rect2(bb_pos, bb_size)
|
||||
|
||||
func _init() -> void:
|
||||
size_flags_changed.connect(update_minimum_size, CONNECT_DEFERRED)
|
||||
|
||||
func _on_mount(control : AnimatableControl) -> void:
|
||||
control.transformation_changed.connect(update_minimum_size, CONNECT_DEFERRED)
|
||||
func _on_unmount(control : AnimatableControl) -> void:
|
||||
control.transformation_changed.disconnect(update_minimum_size)
|
||||
|
||||
## Returns the adjusted size of this mount.
|
||||
func get_relative_size(control : AnimatableControl) -> Vector2:
|
||||
if adjust_scale:
|
||||
return _child_min_size / control.scale
|
||||
return _child_min_size
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,307 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name AnimatedSwitch extends BaseButton
|
||||
## Animated verison of a switch button.
|
||||
|
||||
|
||||
|
||||
@export_group("Redirect")
|
||||
## If [code]true[/code], the switch will be displayed vertical.
|
||||
@export var vertical : bool = false:
|
||||
set(val):
|
||||
if vertical != val:
|
||||
vertical = val
|
||||
force_state(button_pressed)
|
||||
## If [code]true[/code], the switch will be flipped.
|
||||
@export var flip : bool = false:
|
||||
set(val):
|
||||
if flip != val:
|
||||
flip = val
|
||||
force_state(button_pressed)
|
||||
|
||||
@export_group("Size")
|
||||
## The size of the switch's base.
|
||||
@export var switch_size : Vector2 = Vector2(100, 50):
|
||||
set(val):
|
||||
if switch_size != val:
|
||||
switch_size = val
|
||||
_handle_resize()
|
||||
update_minimum_size()
|
||||
## The size of the switch's knob.
|
||||
@export var knob_size : Vector2 = Vector2(40, 40):
|
||||
set(val):
|
||||
if knob_size != val:
|
||||
knob_size = val
|
||||
_handle_resize()
|
||||
update_minimum_size()
|
||||
## The base offset of the knob from it's set position.
|
||||
@export var knob_offset : Vector2 = Vector2.ZERO:
|
||||
set(val):
|
||||
if knob_offset != val:
|
||||
knob_offset = val
|
||||
_handle_resize()
|
||||
update_minimum_size()
|
||||
## An amount of pixels the knob will extend past the switch's base.
|
||||
## [br][br]
|
||||
## Also see [member switch_size].
|
||||
@export var knob_overextend : float = 10:
|
||||
set(val):
|
||||
if knob_overextend != val:
|
||||
knob_overextend = val
|
||||
_handle_resize()
|
||||
update_minimum_size()
|
||||
|
||||
@export_group("Display")
|
||||
## The style of the switch.
|
||||
@export var switch_bg : StyleBox:
|
||||
set(val):
|
||||
if switch_bg != val:
|
||||
switch_bg = val
|
||||
|
||||
if knob_bg:
|
||||
_switch.add_theme_stylebox_override("panel", switch_bg)
|
||||
else:
|
||||
_switch.remove_theme_stylebox_override("panel")
|
||||
## The style of the knob.
|
||||
@export var knob_bg : StyleBox:
|
||||
set(val):
|
||||
if knob_bg != val:
|
||||
knob_bg = val
|
||||
|
||||
if knob_bg:
|
||||
_knob.add_theme_stylebox_override("panel", knob_bg)
|
||||
else:
|
||||
_knob.remove_theme_stylebox_override("panel")
|
||||
|
||||
@export_group("Colors")
|
||||
@export_subgroup("Switch")
|
||||
## The color of the switch's base when unfocused.
|
||||
@export var switch_bg_normal : Color:
|
||||
set(val):
|
||||
if switch_bg_normal != val:
|
||||
switch_bg_normal = val
|
||||
|
||||
_kill_color_animation()
|
||||
_animate_color(false)
|
||||
## The color of the switch's base when focused.
|
||||
@export var switch_bg_focus : Color:
|
||||
set(val):
|
||||
if switch_bg_focus != val:
|
||||
switch_bg_focus = val
|
||||
|
||||
_kill_color_animation()
|
||||
_animate_color(false)
|
||||
## The color of the switch's base when disabled.
|
||||
@export var switch_bg_disabled : Color:
|
||||
set(val):
|
||||
if switch_bg_disabled != val:
|
||||
switch_bg_disabled = val
|
||||
|
||||
_kill_color_animation()
|
||||
_animate_color(false)
|
||||
|
||||
@export_subgroup("Knob")
|
||||
## The color of the switch's knob when unfocused.
|
||||
@export var knob_bg_normal : Color:
|
||||
set(val):
|
||||
if knob_bg_normal != val:
|
||||
knob_bg_normal = val
|
||||
|
||||
_kill_color_animation()
|
||||
_animate_color(false)
|
||||
## The color of the switch's knob when focused.
|
||||
@export var knob_bg_focus : Color:
|
||||
set(val):
|
||||
if knob_bg_focus != val:
|
||||
knob_bg_focus = val
|
||||
|
||||
_kill_color_animation()
|
||||
_animate_color(false)
|
||||
## The color of the switch's knob when disabled.
|
||||
@export var knob_bg_disabled : Color:
|
||||
set(val):
|
||||
if knob_bg_disabled != val:
|
||||
knob_bg_disabled = val
|
||||
|
||||
_kill_color_animation()
|
||||
_animate_color(false)
|
||||
|
||||
|
||||
@export_group("Animation Properties")
|
||||
@export_subgroup("Main")
|
||||
## The ease of the knob's movement across the base.
|
||||
@export var main_ease : Tween.EaseType
|
||||
## The transition of the knob's movement across the base.
|
||||
@export var main_transition : Tween.TransitionType
|
||||
## The duration of the knob's movement across the base.
|
||||
@export_range(0.001, 0.5, 0.001, "or_greater") var main_duration : float = 0.15
|
||||
|
||||
@export_subgroup("Knob Color")
|
||||
## If [code]true[/code], then the knob will change color according to this node's state.
|
||||
@export var animate_knob_color : bool = true
|
||||
## The ease of the knob's color change.
|
||||
@export var knob_color_ease : Tween.EaseType
|
||||
## The transition of the knob's color change.
|
||||
@export var knob_color_transition : Tween.TransitionType
|
||||
## The duration of the knob's color change.
|
||||
@export_range(0.001, 0.5, 0.001, "or_greater") var knob_color_duration : float = 0.1
|
||||
|
||||
@export_subgroup("Switch Color")
|
||||
## If [code]true[/code], then the base will change color according to this node's state.
|
||||
@export var animate_switch_color : bool = true
|
||||
## The ease of the base's color change.
|
||||
@export var switch_color_ease : Tween.EaseType
|
||||
## The transition of the base's color change.
|
||||
@export var switch_color_transition : Tween.TransitionType
|
||||
## The duration of the base's color change.
|
||||
@export_range(0.001, 0.5, 0.001, "or_greater") var switch_color_duration : float = 0.1
|
||||
|
||||
|
||||
|
||||
var _knob : Panel
|
||||
var _switch : Panel
|
||||
|
||||
var _main_animate_tween : Tween
|
||||
var _knob_color_animate_tween : Tween
|
||||
var _switch_color_animate_tween : Tween
|
||||
|
||||
|
||||
|
||||
## Instantly changes the switch's state without animation.
|
||||
func force_state(knob_state : bool) -> void:
|
||||
_handle_animations(false, knob_state)
|
||||
## Changes the switch's state with animation.
|
||||
func toggle_state(knob_state : bool) -> void:
|
||||
_handle_animations(true, knob_state)
|
||||
|
||||
|
||||
## Gets the current knob color.
|
||||
func get_knob_color() -> Color:
|
||||
if disabled:
|
||||
return knob_bg_disabled
|
||||
return knob_bg_focus if button_pressed else knob_bg_normal
|
||||
## Gets the current switch base color.
|
||||
func get_switch_color() -> Color:
|
||||
if disabled:
|
||||
return switch_bg_disabled
|
||||
return switch_bg_focus if button_pressed else switch_bg_normal
|
||||
|
||||
|
||||
|
||||
func _kill_main_animation() -> void:
|
||||
if _main_animate_tween && _main_animate_tween.is_running():
|
||||
_main_animate_tween.kill()
|
||||
func _kill_color_animation() -> void:
|
||||
if _knob_color_animate_tween && _knob_color_animate_tween.is_running():
|
||||
_knob_color_animate_tween.kill()
|
||||
if _switch_color_animate_tween && _switch_color_animate_tween.is_running():
|
||||
_main_animate_tween.kill()
|
||||
|
||||
|
||||
func _handle_animations(animate : bool, knob_state : bool) -> void:
|
||||
_kill_main_animation()
|
||||
_kill_color_animation()
|
||||
button_pressed = knob_state
|
||||
|
||||
if animate:
|
||||
_animate_knob(knob_state)
|
||||
_animate_color(true)
|
||||
return
|
||||
|
||||
_position_knob(float(knob_state))
|
||||
_animate_color(false)
|
||||
|
||||
func _animate_knob(knob_state : bool) -> void:
|
||||
_main_animate_tween = create_tween()
|
||||
_main_animate_tween.set_ease(main_ease)
|
||||
_main_animate_tween.set_trans(main_transition)
|
||||
_main_animate_tween.tween_method(
|
||||
_position_knob,
|
||||
1.0 - float(knob_state),
|
||||
0.0 + float(knob_state),
|
||||
main_duration
|
||||
)
|
||||
func _position_knob(delta : float) -> void:
|
||||
if flip:
|
||||
delta = 1.0 - delta
|
||||
|
||||
var offset : Vector2
|
||||
var delta_v : Vector2
|
||||
if vertical:
|
||||
offset = Vector2(0.0, knob_overextend)
|
||||
delta_v = Vector2(0.5, delta)
|
||||
else:
|
||||
offset = Vector2(knob_overextend, 0.0)
|
||||
delta_v = Vector2(delta, 0.5)
|
||||
|
||||
_knob.position = (
|
||||
(switch_size - knob_size + offset + offset) * delta_v # Size
|
||||
+ (_switch.position - offset) # Position
|
||||
+ knob_offset # Offset
|
||||
)
|
||||
func _animate_color(animate : bool = false) -> void:
|
||||
if animate && animate_knob_color:
|
||||
_knob_color_animate_tween = create_tween()
|
||||
_knob_color_animate_tween.set_ease(knob_color_ease)
|
||||
_knob_color_animate_tween.set_trans(knob_color_transition)
|
||||
_knob_color_animate_tween.tween_property(
|
||||
_knob,
|
||||
"self_modulate",
|
||||
get_knob_color(),
|
||||
knob_color_duration
|
||||
)
|
||||
else:
|
||||
_knob.self_modulate = get_knob_color()
|
||||
|
||||
if animate && animate_switch_color:
|
||||
_switch_color_animate_tween = create_tween()
|
||||
_switch_color_animate_tween.set_ease(switch_color_ease)
|
||||
_switch_color_animate_tween.set_trans(switch_color_transition)
|
||||
_switch_color_animate_tween.tween_property(
|
||||
_switch,
|
||||
"self_modulate",
|
||||
get_switch_color(),
|
||||
switch_color_duration
|
||||
)
|
||||
else:
|
||||
_switch.self_modulate = get_switch_color()
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
toggle_mode = true
|
||||
_switch = Panel.new()
|
||||
_knob = Panel.new()
|
||||
|
||||
_switch.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_knob.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
add_child(_switch)
|
||||
add_child(_knob)
|
||||
|
||||
resized.connect(_handle_resize)
|
||||
toggled.connect(toggle_state)
|
||||
_handle_resize()
|
||||
|
||||
_animate_color(false)
|
||||
func _handle_resize() -> void:
|
||||
_switch.position = (size - switch_size) * 0.5
|
||||
_switch.size = switch_size
|
||||
|
||||
_knob.size = knob_size
|
||||
force_state(button_pressed)
|
||||
|
||||
func _get_minimum_size() -> Vector2:
|
||||
return (knob_size + (knob_offset.abs() * 0.5)).max(switch_size + Vector2(max(0, knob_overextend) * 2, 0))
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name == "toggle_mode":
|
||||
property.usage &= ~PROPERTY_USAGE_EDITOR
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if property == "disabled":
|
||||
disabled = value
|
||||
_animate_color()
|
||||
return true
|
||||
return false
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,178 @@
|
|||
@tool
|
||||
class_name HoldButton extends Control
|
||||
## A [Control] node used for hold buttons.
|
||||
|
||||
|
||||
|
||||
## Emits the state of the button as it is pressed.
|
||||
## [br][br]
|
||||
## Also see [member toggle_mode] and [signal button_state].
|
||||
signal pressed_state(val : bool)
|
||||
## Emits the state of the button as it is released.
|
||||
## [br][br]
|
||||
## Also see [member toggle_mode] and [signal button_state].
|
||||
signal release_state(val : bool)
|
||||
## Emits the state of the button as it is pressed or released.
|
||||
## [br][br]
|
||||
## Also see [signal pressed_state] and [signal release_state].
|
||||
signal button_state(val : bool)
|
||||
|
||||
|
||||
## Emits when button is released with all vaild conditions.
|
||||
## [br][br]
|
||||
## Also see [member release_when_outside] and [member cancel_when_outside].
|
||||
signal press_vaild
|
||||
## Emits when button is released without all vaild conditions.
|
||||
## [br][br]
|
||||
## Also see [member release_when_outside] and [member cancel_when_outside].
|
||||
signal press_invaild
|
||||
|
||||
|
||||
## Emits when press starts.
|
||||
signal press_start
|
||||
## Emits when press ends.
|
||||
signal press_end
|
||||
|
||||
|
||||
|
||||
## If [code]true[/code], the button's state is pressed. Means the button is pressed down
|
||||
## or toggled (if [member toggle_mode] is active). Only works if [member toggle_mode] is
|
||||
## [code]false[/code].
|
||||
@export var button_pressed : bool
|
||||
## If [code]true[/code], the button is in [member toggle_mode]. Makes the button
|
||||
## flip state between pressed and unpressed each time its area is clicked.
|
||||
@export var toggle_mode : bool:
|
||||
set(val):
|
||||
if toggle_mode != val:
|
||||
toggle_mode = val
|
||||
if !val:
|
||||
button_pressed = false
|
||||
|
||||
notify_property_list_changed()
|
||||
## If [code]true[/code], then this node does not accept input.
|
||||
@export var disabled : bool:
|
||||
set(val):
|
||||
if disabled != val:
|
||||
disabled = val
|
||||
_bounds_check.disabled = val
|
||||
_distance_check.disabled = val
|
||||
|
||||
|
||||
@export_group("Release At")
|
||||
## If [code]true[/code], the button's held state is released if input moves outside of
|
||||
## bounds.
|
||||
@export var release_when_outside : bool = true:
|
||||
set(val):
|
||||
release_when_outside = val
|
||||
_bounds_check.release_when_outside = val
|
||||
## If [code]true[/code], the button's held state is released and all checking is stopped
|
||||
## if input moves outside of bounds.
|
||||
@export var cancel_when_outside : bool = true:
|
||||
set(val):
|
||||
cancel_when_outside = val
|
||||
_bounds_check.cancel_when_outside = val
|
||||
|
||||
@export_group("Release On Drag")
|
||||
## The current check mode.
|
||||
##
|
||||
## Also see [enum CHECK_MODE].
|
||||
@export var mode : DistanceCheck.CHECK_MODE = DistanceCheck.CHECK_MODE.BOTH:
|
||||
set(val):
|
||||
mode = val
|
||||
_distance_check.mode = val
|
||||
## The max pixels difference, between the start and current position, that can be tolerated.
|
||||
@export var distance : float = 30:
|
||||
set(val):
|
||||
distance = val
|
||||
_distance_check.distance = val
|
||||
|
||||
|
||||
var _bounds_check : BoundsCheck
|
||||
var _distance_check : DistanceCheck
|
||||
|
||||
|
||||
|
||||
## Forcibly stops this node's check.
|
||||
func force_release() -> void:
|
||||
_bounds_check.force_release()
|
||||
_distance_check.force_release()
|
||||
|
||||
_on_end_invaild()
|
||||
## Returns if mouse or touch is being held (mouse or touch outside of limit without being released).
|
||||
## [br][br]
|
||||
## Also see [method force_release].
|
||||
func is_held() -> bool:
|
||||
return _bounds_check.is_checking()
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_distance_check = DistanceCheck.new()
|
||||
_distance_check.name = "distance_check"
|
||||
_distance_check.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(_distance_check)
|
||||
|
||||
_distance_check.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_distance_check.cancel_when_outside = true
|
||||
_distance_check.disabled = disabled
|
||||
_distance_check.mode = mode
|
||||
_distance_check.distance = distance
|
||||
|
||||
_distance_check.pos_exceeded.connect(force_release)
|
||||
|
||||
|
||||
_bounds_check = BoundsCheck.new()
|
||||
_bounds_check.name = "bounds_check"
|
||||
_bounds_check.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(_bounds_check)
|
||||
|
||||
_bounds_check.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_bounds_check.disabled = disabled
|
||||
_bounds_check.release_when_outside = release_when_outside
|
||||
_bounds_check.cancel_when_outside = cancel_when_outside
|
||||
|
||||
_bounds_check.end_vaild.connect(_on_end_vaild)
|
||||
_bounds_check.end_invaild.connect(_on_end_invaild)
|
||||
_bounds_check.start_check.connect(_on_start_check)
|
||||
_bounds_check.end_check.connect(_on_end_check)
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name == "button_pressed":
|
||||
if !toggle_mode:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion || event is InputEventScreenDrag || event is InputEventMouseButton || event is InputEventScreenTouch:
|
||||
event.position += global_position
|
||||
_bounds_check._gui_input(event)
|
||||
_distance_check._gui_input(event)
|
||||
|
||||
|
||||
func _on_start_check() -> void:
|
||||
press_start.emit()
|
||||
|
||||
if toggle_mode:
|
||||
button_pressed = !button_pressed
|
||||
else:
|
||||
button_pressed = true
|
||||
button_state.emit(button_pressed)
|
||||
pressed_state.emit(button_pressed)
|
||||
func _on_end_check() -> void:
|
||||
_distance_check.force_release()
|
||||
press_end.emit()
|
||||
func _on_end_vaild() -> void:
|
||||
press_vaild.emit()
|
||||
|
||||
if !toggle_mode:
|
||||
button_pressed = false
|
||||
button_state.emit(button_pressed)
|
||||
release_state.emit(button_pressed)
|
||||
func _on_end_invaild() -> void:
|
||||
press_invaild.emit()
|
||||
|
||||
if toggle_mode:
|
||||
button_pressed = !button_pressed
|
||||
else:
|
||||
button_pressed = false
|
||||
button_state.emit(button_pressed)
|
||||
release_state.emit(button_pressed)
|
|
@ -0,0 +1,9 @@
|
|||
@tool
|
||||
class_name BoundsCheck extends MotionCheck
|
||||
## A [Control] node used to check if a mouse or touch moved outside this node's bounds after
|
||||
## a vaild press inside.
|
||||
|
||||
|
||||
|
||||
func _pos_check(pos : Vector2) -> bool:
|
||||
return get_global_rect().has_point(pos)
|
|
@ -0,0 +1,46 @@
|
|||
@tool
|
||||
class_name DistanceCheck extends MotionCheck
|
||||
## A [Control] node used to check if a mouse or touch moved a distance away from where
|
||||
## the user originally pressed.
|
||||
|
||||
|
||||
|
||||
## How this node will determine the distance between the starting and current location
|
||||
## of mouse and touch movement.
|
||||
enum CHECK_MODE {
|
||||
NONE = 0b000, ## No action.
|
||||
HORIZONTAL = 0b001, ## Only checks the horizontal difference.
|
||||
VERTICAL = 0b010, ## Only checks the vertical difference.
|
||||
BOTH = 0b011, ## Only checks orthogonal difference.
|
||||
DISTANCE = 0b100 ## Uses the Pythagorean Theorem.
|
||||
}
|
||||
|
||||
|
||||
## The current check mode.
|
||||
##
|
||||
## Also see [enum CHECK_MODE].
|
||||
@export var mode : CHECK_MODE
|
||||
## The max pixels difference, between the start and current position, that can be tolerated.
|
||||
@export var distance : float = 30
|
||||
|
||||
|
||||
|
||||
var _prev_pos : Vector2
|
||||
|
||||
|
||||
func _pos_check(pos : Vector2) -> bool:
|
||||
if mode & CHECK_MODE.DISTANCE:
|
||||
if _prev_pos.distance_squared_to(pos) > distance * distance:
|
||||
return false
|
||||
return true
|
||||
|
||||
var diff := (_prev_pos - pos).abs()
|
||||
if mode & CHECK_MODE.HORIZONTAL:
|
||||
if diff.x > distance:
|
||||
return false
|
||||
if mode & CHECK_MODE.VERTICAL:
|
||||
if diff.y > distance:
|
||||
return false
|
||||
return true
|
||||
func _on_check_start(event: InputEvent) -> void:
|
||||
_prev_pos = event.position
|
|
@ -0,0 +1,165 @@
|
|||
@tool
|
||||
class_name MotionCheck extends Control
|
||||
## Checks for motion of the mouse or touch input after a press.
|
||||
|
||||
## Emited when check has started (mouse or touch is pressed).
|
||||
signal start_check
|
||||
## Emited when check has ended (mouse or touch is released or distance limit has exceeded).
|
||||
signal end_check
|
||||
|
||||
## Emited when mouse or touch is released within the distence limit.
|
||||
signal end_vaild
|
||||
## Emited when check has ended without mouse or touch being released within the distence limit.
|
||||
## [br][br]
|
||||
## Also see [member cancel_when_outside] and [method _pos_check].
|
||||
signal end_invaild
|
||||
|
||||
## Emited when mouse or touch is moved outside the distence limit.
|
||||
## [br][br]
|
||||
## Also see [member cancel_when_outside] and [method _pos_check].
|
||||
signal pos_exceeded
|
||||
## Emited when the current held state changes.
|
||||
signal held_state(state : bool)
|
||||
|
||||
|
||||
## If [code]true[/code], the button's held state is released if input moves outside of
|
||||
## bounds.
|
||||
## [br][br]
|
||||
## Also see [member cancel_when_outside] and [method _pos_check].
|
||||
@export var release_when_outside : bool = false:
|
||||
set(val):
|
||||
if release_when_outside != val:
|
||||
release_when_outside = val
|
||||
## If [code]true[/code], then the check will end when mouse or touch is moved outside the distence limit.
|
||||
## [br][br]
|
||||
## Also see [member release_when_outside] and [method _pos_check].
|
||||
@export var cancel_when_outside : bool = false:
|
||||
set(val):
|
||||
if cancel_when_outside != val:
|
||||
cancel_when_outside = val
|
||||
## If [code]true[/code], then this node does not accept input.
|
||||
@export var disabled : bool:
|
||||
set(val):
|
||||
if disabled != val:
|
||||
disabled = val
|
||||
if val:
|
||||
force_release()
|
||||
|
||||
|
||||
var _checking : bool = false
|
||||
var _holding : bool = false
|
||||
|
||||
|
||||
# Public Methods
|
||||
|
||||
## Forcibly stops this node's check.
|
||||
func force_release() -> void:
|
||||
if _holding:
|
||||
held_state.emit(false)
|
||||
_holding = false
|
||||
if _checking:
|
||||
_invaild_end()
|
||||
_checking = false
|
||||
## Returns if this node is currently checking a mouse or touch press.
|
||||
func is_checking() -> bool: return _checking
|
||||
## Returns if mouse or touch is being held (mouse or touch outside of limit without being released).
|
||||
## [br][br]
|
||||
## Also see [method force_release].
|
||||
func is_held() -> bool: return _holding
|
||||
|
||||
|
||||
# Virtual Methods
|
||||
|
||||
## A virtual method that should be overloaded. Returns [code]true[/code] if [param pos] is
|
||||
## within the distance limit. [code]false[/code] otherwise.
|
||||
func _pos_check(pos : Vector2) -> bool:
|
||||
return false
|
||||
## A virtual method that should be overloaded. This is called when an input starts a
|
||||
## check.
|
||||
func _on_check_start(event: InputEvent) -> void:
|
||||
pass
|
||||
## A virtual method that should be overloaded. This is called when an input releases a
|
||||
## check.
|
||||
func _on_check_release(event: InputEvent) -> void:
|
||||
pass
|
||||
## A virtual method that should be overloaded. This is called when an input exceeds a
|
||||
## check.
|
||||
## [br][br]
|
||||
## Also see [method _pos_check].
|
||||
func _on_check_exceeded(event: InputEvent) -> void:
|
||||
pass
|
||||
|
||||
|
||||
# Private Methods
|
||||
|
||||
func _init() -> void:
|
||||
mouse_filter = MOUSE_FILTER_PASS
|
||||
tree_exiting.connect(force_release)
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
if property == "mouse_filter":
|
||||
return mouse_filter == MOUSE_FILTER_PASS
|
||||
return false
|
||||
func _property_get_revert(property: StringName) -> Variant:
|
||||
if property == "mouse_filter":
|
||||
return MOUSE_FILTER_PASS
|
||||
return null
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if disabled: return
|
||||
|
||||
if event is InputEventMouseButton || event is InputEventScreenTouch:
|
||||
if event.pressed:
|
||||
if !_checking && get_global_rect().has_point(event.position):
|
||||
_on_check_start(event)
|
||||
|
||||
_checking = true
|
||||
|
||||
held_state.emit(true)
|
||||
start_check.emit()
|
||||
elif _checking:
|
||||
_on_check_release(event)
|
||||
|
||||
_checking = false
|
||||
if !_holding:
|
||||
held_state.emit(false)
|
||||
else:
|
||||
_holding = false
|
||||
|
||||
if _pos_check(event.position):
|
||||
_vaild_end()
|
||||
else:
|
||||
_invaild_end()
|
||||
if mouse_filter == MOUSE_FILTER_STOP: accept_event()
|
||||
|
||||
if _checking:
|
||||
if event is InputEventMouseMotion || event is InputEventScreenDrag:
|
||||
if _holding:
|
||||
if _pos_check(event.position):
|
||||
held_state.emit(true)
|
||||
_checking = true
|
||||
_holding = false
|
||||
return
|
||||
|
||||
if !_pos_check(event.position):
|
||||
pos_exceeded.emit()
|
||||
if release_when_outside:
|
||||
held_state.emit(false)
|
||||
_holding = true
|
||||
|
||||
if cancel_when_outside:
|
||||
_on_check_exceeded(event)
|
||||
_holding = false
|
||||
_checking = false
|
||||
|
||||
_invaild_end()
|
||||
return
|
||||
|
||||
if mouse_filter == MOUSE_FILTER_STOP: accept_event()
|
||||
|
||||
func _invaild_end() -> void:
|
||||
end_invaild.emit()
|
||||
end_check.emit()
|
||||
func _vaild_end() -> void:
|
||||
end_vaild.emit()
|
||||
end_check.emit()
|
|
@ -0,0 +1,151 @@
|
|||
@tool
|
||||
class_name ModulateTransitionButton extends ModulateTransitionContainer
|
||||
## A button that inherts from [ModulateTransitionContainer] and uses [HoldButton] as
|
||||
## input.
|
||||
|
||||
|
||||
|
||||
## Emits the state of the button as it is released.
|
||||
signal release_state(toggle : bool)
|
||||
|
||||
## Emits when button is released with all vaild conditions.
|
||||
signal press_vaild
|
||||
|
||||
## Emits when press starts.
|
||||
signal press_start
|
||||
## Emits when press ends.
|
||||
signal press_end
|
||||
|
||||
|
||||
|
||||
@export_group("Toggleable")
|
||||
## If [code]true[/code], the button's state is pressed. Means the button is pressed down
|
||||
## or toggled (if [member toggle_mode] is active). Only works if [member toggle_mode] is
|
||||
## [code]false[/code].
|
||||
@export var button_pressed : bool:
|
||||
set(val):
|
||||
if button_pressed != val:
|
||||
button_pressed = val
|
||||
_set_button_color(val)
|
||||
## If [code]true[/code], the button is in [member toggle_mode]. Makes the button
|
||||
## flip state between pressed and unpressed each time its area is clicked.
|
||||
@export var toggle_mode : bool:
|
||||
set(val):
|
||||
if toggle_mode != val:
|
||||
toggle_mode = val
|
||||
button_pressed = false
|
||||
notify_property_list_changed()
|
||||
|
||||
if _button: _button.toggle_mode = val
|
||||
## If [code]true[/code], then this node does not accept input.
|
||||
@export var disabled : bool:
|
||||
set = _set_disabled
|
||||
|
||||
@export_group("Alpha")
|
||||
## The color to modulate to when this node is unfocused.
|
||||
@export var normal_color : Color = Color(1.0, 1.0, 1.0, 1.0):
|
||||
set(val):
|
||||
if normal_color != val:
|
||||
normal_color = val
|
||||
if is_node_ready(): colors[0] = val
|
||||
force_color(focused_color)
|
||||
## The color to modulate to when this node is focused.
|
||||
@export var focus_color : Color = Color(1.0, 1.0, 1.0, 0.75):
|
||||
set(val):
|
||||
if focus_color != val:
|
||||
focus_color = val
|
||||
if is_node_ready(): colors[1] = val
|
||||
force_color(focused_color)
|
||||
## The color to modulate to when this node is disabled.
|
||||
@export var disabled_color : Color = Color(1.0, 1.0, 1.0, 0.5):
|
||||
set(val):
|
||||
if disabled_color != val:
|
||||
disabled_color = val
|
||||
if is_node_ready(): colors[2] = val
|
||||
force_color(focused_color)
|
||||
|
||||
|
||||
var _button : HoldButton
|
||||
|
||||
|
||||
|
||||
## Forcibly stops this node's check.
|
||||
func force_release() -> void:
|
||||
if _button: _button.force_release()
|
||||
## Returns if mouse or touch is being held (mouse or touch outside of limit without being released).
|
||||
|
||||
func is_held() -> bool:
|
||||
return _button && _button.is_held()
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
super()
|
||||
|
||||
_button = HoldButton.new()
|
||||
add_child(_button)
|
||||
_button.move_to_front()
|
||||
|
||||
if !Engine.is_editor_hint():
|
||||
child_order_changed.connect(_button.move_to_front, CONNECT_DEFERRED)
|
||||
|
||||
_button.button_state.connect(_set_button_color)
|
||||
_button.release_state.connect(_emit_vaild_release)
|
||||
|
||||
_button.press_start.connect(press_start.emit)
|
||||
_button.press_end.connect(press_end.emit)
|
||||
_button.press_vaild.connect(press_vaild.emit)
|
||||
|
||||
_button.mouse_filter = mouse_filter
|
||||
_button.mouse_force_pass_scroll_events = mouse_force_pass_scroll_events
|
||||
_button.mouse_default_cursor_shape = mouse_default_cursor_shape
|
||||
|
||||
_button.toggle_mode = toggle_mode
|
||||
_button.button_pressed = button_pressed
|
||||
_button.disabled = disabled
|
||||
func _ready() -> void:
|
||||
colors = [normal_color, focus_color, disabled_color]
|
||||
force_color(2 if disabled else (1 if button_pressed else 0))
|
||||
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
match property.name:
|
||||
"pressed":
|
||||
if !toggle_mode:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
"focused_alpha", "alphas":
|
||||
property.usage &= ~PROPERTY_USAGE_EDITOR
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if _button:
|
||||
match property:
|
||||
"mouse_filter":
|
||||
_button.mouse_filter = value
|
||||
"mouse_force_pass_scroll_events":
|
||||
_button.mouse_force_pass_scroll_events = value
|
||||
"mouse_default_cursor_shape":
|
||||
_button.mouse_default_cursor_shape = value
|
||||
return false
|
||||
func _get(property: StringName) -> Variant:
|
||||
if _button:
|
||||
match property:
|
||||
"mouse_filter":
|
||||
return _button.mouse_filter
|
||||
"mouse_force_pass_scroll_events":
|
||||
return _button.mouse_force_pass_scroll_events
|
||||
"mouse_default_cursor_shape":
|
||||
return _button.mouse_default_cursor_shape
|
||||
return null
|
||||
|
||||
func _set_disabled(val : bool) -> void:
|
||||
disabled = val
|
||||
|
||||
_set_button_color(button_pressed)
|
||||
if _button:
|
||||
_button.disabled = disabled
|
||||
func _set_button_color(val : bool) -> void:
|
||||
if disabled: set_color(2)
|
||||
else: set_color(int(val))
|
||||
|
||||
func _emit_vaild_release(release : bool) -> void:
|
||||
_set_button_color(release)
|
||||
release_state.emit(release)
|
|
@ -0,0 +1,158 @@
|
|||
@tool
|
||||
class_name StyleTransitionButton extends StyleTransitionContainer
|
||||
## A button that inherts from [StyleTransitionContainer] and uses [HoldButton] as
|
||||
## input.
|
||||
|
||||
|
||||
|
||||
## Emits the state of the button as it is released.
|
||||
signal release_state(toggle : bool)
|
||||
|
||||
## Emits when button is released with all vaild conditions.
|
||||
signal press_vaild
|
||||
|
||||
## Emits when press starts.
|
||||
signal press_start
|
||||
## Emits when press ends.
|
||||
signal press_end
|
||||
|
||||
|
||||
|
||||
@export_group("Toggleable")
|
||||
## If [code]true[/code], the button's state is pressed. Means the button is pressed down
|
||||
## or toggled (if [member toggle_mode] is active). Only works if [member toggle_mode] is
|
||||
## [code]false[/code].
|
||||
@export var button_pressed : bool:
|
||||
set(val):
|
||||
if button_pressed != val:
|
||||
button_pressed = val
|
||||
_set_button_color(val)
|
||||
## If [code]true[/code], the button is in [member toggle_mode]. Makes the button
|
||||
## flip state between pressed and unpressed each time its area is clicked.
|
||||
@export var toggle_mode : bool:
|
||||
set(val):
|
||||
if toggle_mode != val:
|
||||
toggle_mode = val
|
||||
button_pressed = false
|
||||
notify_property_list_changed()
|
||||
|
||||
if _button: _button.toggle_mode = val
|
||||
var _disabled : bool:
|
||||
set = _set_disabled
|
||||
## If [code]true[/code], then this node does not accept input.
|
||||
@export var disabled : bool:
|
||||
set(val):
|
||||
if disabled != val:
|
||||
disabled = val
|
||||
_set_disabled(val)
|
||||
|
||||
@export_group("Colors")
|
||||
## The color to modulate to when this node is unfocused.
|
||||
@export var normal_color : Color = Color(0.525, 0.329, 0.808):
|
||||
set(val):
|
||||
if normal_color != val:
|
||||
normal_color = val
|
||||
if is_node_ready(): colors[0] = val
|
||||
force_color(focused_color)
|
||||
## The color to modulate to when this node is focused.
|
||||
@export var focus_color : Color = Color(0.611, 0.441, 0.886):
|
||||
set(val):
|
||||
if focus_color != val:
|
||||
focus_color = val
|
||||
if is_node_ready(): colors[1] = val
|
||||
force_color(focused_color)
|
||||
## The color to modulate to when this node is disabled.
|
||||
@export var disabled_color : Color = Color(0.318, 0.247, 0.565):
|
||||
set(val):
|
||||
if disabled_color != val:
|
||||
disabled_color = val
|
||||
if is_node_ready(): colors[2] = val
|
||||
force_color(focused_color)
|
||||
|
||||
|
||||
var _button : HoldButton
|
||||
|
||||
|
||||
|
||||
## Forcibly stops this node's check.
|
||||
func force_release() -> void:
|
||||
if _button: _button.force_release()
|
||||
## Returns if mouse or touch is being held (mouse or touch outside of limit without being released).
|
||||
func is_held() -> bool:
|
||||
return _button && _button.is_held()
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_button = HoldButton.new()
|
||||
add_child(_button)
|
||||
|
||||
_button.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
_button.move_to_front()
|
||||
|
||||
if !Engine.is_editor_hint():
|
||||
child_order_changed.connect(_button.move_to_front, CONNECT_DEFERRED)
|
||||
|
||||
_button.button_state.connect(_set_button_color)
|
||||
_button.release_state.connect(_emit_vaild_release)
|
||||
|
||||
_button.press_start.connect(press_start.emit)
|
||||
_button.press_end.connect(press_end.emit)
|
||||
_button.press_vaild.connect(press_vaild.emit)
|
||||
|
||||
_button.mouse_filter = mouse_filter
|
||||
_button.mouse_force_pass_scroll_events = mouse_force_pass_scroll_events
|
||||
_button.mouse_default_cursor_shape = mouse_default_cursor_shape
|
||||
|
||||
_button.toggle_mode = toggle_mode
|
||||
_button.button_pressed = button_pressed
|
||||
_button.disabled = _disabled
|
||||
func _ready() -> void:
|
||||
super()
|
||||
colors = [normal_color, focus_color, disabled_color]
|
||||
force_color(2 if _disabled else (1 if button_pressed else 0))
|
||||
|
||||
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
match property.name:
|
||||
"pressed":
|
||||
if !toggle_mode:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
"focused_color", "colors":
|
||||
property.usage &= ~PROPERTY_USAGE_EDITOR
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if _button:
|
||||
match property:
|
||||
"mouse_filter":
|
||||
_button.mouse_filter = value
|
||||
"mouse_force_pass_scroll_events":
|
||||
_button.mouse_force_pass_scroll_events = value
|
||||
"mouse_default_cursor_shape":
|
||||
_button.mouse_default_cursor_shape = value
|
||||
return false
|
||||
func _get(property: StringName) -> Variant:
|
||||
if _button:
|
||||
match property:
|
||||
"mouse_filter":
|
||||
return _button.mouse_filter
|
||||
"mouse_force_pass_scroll_events":
|
||||
return _button.mouse_force_pass_scroll_events
|
||||
"mouse_default_cursor_shape":
|
||||
return _button.mouse_default_cursor_shape
|
||||
return null
|
||||
|
||||
func _set_disabled(val : bool) -> void:
|
||||
_disabled = val || disabled
|
||||
|
||||
_set_button_color(button_pressed)
|
||||
if _button:
|
||||
_button.disabled = _disabled
|
||||
func _set_button_color(val : bool) -> void:
|
||||
if _disabled: set_color(2)
|
||||
else: set_color(int(val))
|
||||
|
||||
|
||||
func _emit_vaild_release(release : bool) -> void:
|
||||
_set_button_color(release)
|
||||
release_state.emit(release)
|
616
godot/addons/FreeControl/src/CustomClasses/Carousel/Carousel.gd
Normal file
|
@ -0,0 +1,616 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name Carousel extends Container
|
||||
## A container for Carousel Display of [Control] nodes.
|
||||
|
||||
|
||||
|
||||
## Changes the behavior of how draging scrolls the carousel items. Also see [member snap_carousel_transtion_type], [member snap_carousel_ease_type], and [member paging_requirement].
|
||||
enum SNAP_BEHAVIOR {
|
||||
NONE = 0b00, ## No behavior.
|
||||
SNAP = 0b01, ## Once drag is released, the carousel will snap to the nearest item.x
|
||||
PAGING = 0b10 ## Carousel items will not scroll when dragged, unless [member paging_requirement] threshold is met. [member hard_stop] will be assumed as [code]true[/code] for this.
|
||||
}
|
||||
## Internel enum used to differentiate what animation is currently playing
|
||||
enum ANIMATION_TYPE {
|
||||
NONE = 0b00, ## No behavior.
|
||||
MANUAL = 0b01, ## Currently animating via request by [method go_to_index].
|
||||
SNAP = 0b10 ## Currently animating via an auto-item snapping request.
|
||||
}
|
||||
|
||||
|
||||
## This signal is emited when a snap reaches it's destination.
|
||||
signal snap_end
|
||||
## This signal is emited when a snap begins.
|
||||
signal snap_begin
|
||||
## This signal is emited when a drag finishes. This does not include the slowdown caused when [member hard_stop] is [code]false[/code].
|
||||
signal drag_end
|
||||
## This signal is emited when a drag begins.
|
||||
signal drag_begin
|
||||
## This signal is emited when the slowdown, caused when [member hard_stop] is [code]false[/code], finished naturally.
|
||||
signal slowdown_end
|
||||
## This signal is emited when the slowdown, caused when [member hard_stop] is [code]false[/code], is interrupted by another drag or other feature.
|
||||
signal slowdown_interupted
|
||||
|
||||
|
||||
|
||||
@export_group("Carousel Options")
|
||||
## The index of the item this carousel will start at.
|
||||
@export var starting_index : int = 0:
|
||||
set(val):
|
||||
if starting_index != val:
|
||||
starting_index = val
|
||||
go_to_index(-val, false)
|
||||
## The size of each item in the carousel.
|
||||
@export var item_size : Vector2 = Vector2(200, 200):
|
||||
set(val):
|
||||
if item_size != val:
|
||||
var current_index := get_carousel_index()
|
||||
item_size = val
|
||||
_settup_children()
|
||||
go_to_index(current_index, false)
|
||||
## The space between each item in the carousel.
|
||||
@export var item_seperation : int = 0:
|
||||
set(val):
|
||||
if item_seperation != val:
|
||||
item_seperation = val
|
||||
_kill_animation()
|
||||
_adjust_children()
|
||||
## The orientation the carousel items will be displayed in.
|
||||
@export_range(0, 360, 0.001, "or_less", "or_greater") var carousel_angle : float = 0.0:
|
||||
set(val):
|
||||
if carousel_angle != val:
|
||||
var current_index := get_carousel_index()
|
||||
|
||||
carousel_angle = val
|
||||
_angle_vec = Vector2.RIGHT.rotated(deg_to_rad(carousel_angle))
|
||||
|
||||
_kill_animation()
|
||||
_adjust_children()
|
||||
go_to_index(current_index, false)
|
||||
|
||||
@export_group("Loop Options")
|
||||
## Allows looping from the last item to the first and vice versa.
|
||||
@export var allow_loop : bool = true
|
||||
## If [code]true[/code], the carousel will display it's items as if looping. Otherwise, the items will not loop.
|
||||
## [br][br]
|
||||
## also see [member enforce_border] and [member border_limit].
|
||||
@export var display_loop : bool = true:
|
||||
set(val):
|
||||
if val != display_loop:
|
||||
display_loop = val
|
||||
_adjust_children()
|
||||
notify_property_list_changed()
|
||||
## The number of items, surrounding the current item of the current index, that will be visible.
|
||||
## If [code]-1[/code], all items will be visible.
|
||||
@export var display_range : int = -1:
|
||||
set(val):
|
||||
val = max(-1, val)
|
||||
if val != display_range:
|
||||
display_range = val
|
||||
_adjust_children()
|
||||
|
||||
@export_group("Snap")
|
||||
## Assigns the behavior of how draging scrolls the carousel items. Also see [member snap_carousel_transtion_type], [member snap_carousel_ease_type], and [member paging_requirement].
|
||||
@export var snap_behavior : SNAP_BEHAVIOR = SNAP_BEHAVIOR.SNAP:
|
||||
set(val):
|
||||
if val != snap_behavior:
|
||||
snap_behavior = val
|
||||
_end_drag_slowdown()
|
||||
_create_animation(get_carousel_index(), ANIMATION_TYPE.SNAP)
|
||||
notify_property_list_changed()
|
||||
## If [member snap_behavior] is [SNAP_BEHAVIOR.PAGING], this is the draging threshold needed to page to the next carousel item.
|
||||
@export var paging_requirement : int = 200:
|
||||
set(val):
|
||||
val = max(1, val)
|
||||
if val != paging_requirement:
|
||||
paging_requirement = val
|
||||
_adjust_children()
|
||||
|
||||
@export_group("Animation Options")
|
||||
@export_subgroup("Manual")
|
||||
## The duration of the animation any call to [method go_to_index] will cause, if the animation option is requested.
|
||||
@export_range(0.001, 2.0, 0.001, "or_greater") var manual_carousel_duration : float = 0.4
|
||||
## The [enum Tween.TransitionType] of the animation any call to [method go_to_index] will cause, if the animation option is requested.
|
||||
@export var manual_carousel_transtion_type : Tween.TransitionType
|
||||
## The [enum Tween.EaseType] of the animation any call to [method go_to_index] will cause, if the animation option is requested.
|
||||
@export var manual_carousel_ease_type : Tween.EaseType
|
||||
|
||||
@export_subgroup("Snap")
|
||||
## The duration of the animation when snapping to an item.
|
||||
@export_range(0.001, 2.0, 0.001, "or_greater") var snap_carousel_duration : float = 0.2
|
||||
## The [enum Tween.TransitionType] of the animation when snapping to an item.
|
||||
@export var snap_carousel_transtion_type : Tween.TransitionType
|
||||
## The [enum Tween.EaseType] of the animation when snapping to an item.
|
||||
@export var snap_carousel_ease_type : Tween.EaseType
|
||||
|
||||
@export_group("Drag")
|
||||
## If [code]true[/code], the user is allowed to drag via their mouse or touch.
|
||||
@export var can_drag : bool = true:
|
||||
set(val):
|
||||
if val != can_drag:
|
||||
can_drag = val
|
||||
notify_property_list_changed()
|
||||
if !val:
|
||||
_drag_scroll_value = 0
|
||||
if _is_dragging:
|
||||
_adjust_children()
|
||||
## If [code]true[/code], the user is allowed to drag outisde the drawer's bounding box.
|
||||
## [br][br]
|
||||
## Also see [member can_drag].
|
||||
@export var drag_outside : bool = false:
|
||||
set(val):
|
||||
if val != drag_outside:
|
||||
drag_outside = val
|
||||
@export_subgroup("Limits")
|
||||
## The max amount a user can drag in either direction. If [code]0[/code], then the user can drag any amount they wish.
|
||||
@export var drag_limit : int = 0:
|
||||
set(val):
|
||||
val = max(0, val)
|
||||
if val != drag_limit: drag_limit = val
|
||||
## When dragging, the user will not be able to move past the last or first item, besides for [member border_limit] number of extra pixels.
|
||||
## [br][br]
|
||||
## This value is assumed [code]false[/code] is [member display_loop] is [code]true[/code].
|
||||
@export var enforce_border : bool = false:
|
||||
set(val):
|
||||
if val != enforce_border:
|
||||
enforce_border = val
|
||||
_adjust_children()
|
||||
notify_property_list_changed()
|
||||
## The amount of extra pixels a user can drag past the last and before the first item in the carousel.
|
||||
## [br][br]
|
||||
## This property does nothing if enforce_border is [code]false[/code].
|
||||
@export var border_limit : int = 0:
|
||||
set(val):
|
||||
if val != border_limit:
|
||||
border_limit = val
|
||||
_adjust_children()
|
||||
|
||||
@export_subgroup("Slowdown")
|
||||
## If [code]true[/code] the carousel will immediately stop when not being dragged. Otherwise, drag speed will be gradually decreased.
|
||||
## [br][br]
|
||||
## This property is assumed [code]true[/code] if [member snap_behavior] is set to [SNAP_BEHAVIOR.PAGING]. Also see [member slowdown_drag], [member slowdown_friction], and [member slowdown_cutoff].
|
||||
@export var hard_stop : bool = true:
|
||||
set(val):
|
||||
if val != hard_stop:
|
||||
hard_stop = val
|
||||
_end_drag_slowdown()
|
||||
notify_property_list_changed()
|
||||
## The percentage multiplier the drag velocity will experience each frame.
|
||||
## [br][br]
|
||||
## This property does nothing if [member hard_stop] is [code]true[/code].
|
||||
@export_range(0.0, 1.0, 0.001) var slowdown_drag : float = 0.9
|
||||
## The constant decrease the drag velocity will experience each frame.
|
||||
## [br][br]
|
||||
## This property does nothing if [member hard_stop] is [code]true[/code].
|
||||
@export_range(0.0, 5.0, 0.001, "or_greater", "hide_slider") var slowdown_friction : float = 0.1
|
||||
## The cutoff amount. If drag velocity magnitude drops below this amount, the slowdown has finished.
|
||||
## [br][br]
|
||||
## This property does nothing if [member hard_stop] is [code]true[/code].
|
||||
@export_range(0.01, 10.0, 0.001, "or_greater", "hide_slider") var slowdown_cutoff : float = 0.01
|
||||
|
||||
|
||||
var _scroll_value : int
|
||||
var _drag_scroll_value : int
|
||||
var _drag_velocity : float
|
||||
|
||||
var _item_count : int = 0
|
||||
var _item_infos : Array
|
||||
|
||||
var _scroll_tween : Tween
|
||||
var _is_dragging : bool = false
|
||||
|
||||
var _last_animation : ANIMATION_TYPE = ANIMATION_TYPE.NONE
|
||||
|
||||
var _angle_vec : Vector2
|
||||
var _mouse_checking : bool
|
||||
|
||||
|
||||
|
||||
|
||||
# Public Functions
|
||||
|
||||
## Gets the index of the current carousel item.[br]
|
||||
## If [param with_drag] is [code]true[/code] the current drag will also be considered.[br]
|
||||
## If [param with_clamp] is [code]true[/code] the index will be looped if [member allow_loop] is true or clamped to a vaild index within the carousel.
|
||||
func get_carousel_index(with_drag : bool = false, with_clamp : bool = true) -> int:
|
||||
if _item_count == 0: return -1
|
||||
|
||||
var scroll : int = _scroll_value
|
||||
if with_drag: scroll += _drag_scroll_value
|
||||
|
||||
var calculated := floori((float(scroll) / float(_get_relevant_axis())) + 0.5)
|
||||
if with_clamp:
|
||||
if allow_loop:
|
||||
calculated = posmod(calculated, _item_count)
|
||||
else:
|
||||
calculated = clampi(calculated, 0, _item_count - 1)
|
||||
|
||||
return calculated
|
||||
## Moves to an item of the given index within the carousel. If an invalid index is given, it will be posmod into a vaild index.
|
||||
func go_to_index(idx : int, animation : bool = true) -> void:
|
||||
if _item_count == 0: return
|
||||
|
||||
if allow_loop:
|
||||
idx = (((idx % _item_count) - _item_count) % _item_count)
|
||||
else:
|
||||
idx = clamp(idx, 0, _item_count - 1)
|
||||
|
||||
if animation:
|
||||
_create_animation(idx, ANIMATION_TYPE.MANUAL)
|
||||
else:
|
||||
_kill_animation()
|
||||
_scroll_value = -_get_relevant_axis() * idx
|
||||
_adjust_children()
|
||||
## Moves to the previous item in the carousel, if there is one.
|
||||
func prev(animation : bool = true) -> void:
|
||||
go_to_index(get_carousel_index() - 1, animation)
|
||||
## Moves to the next item in the carousel, if there is one.
|
||||
func next(animation : bool = true) -> void:
|
||||
go_to_index(get_carousel_index() + 1, animation)
|
||||
## Enacts a manual drag on the carousel. This can be used even if [member can_drag] is [code]false[/code].
|
||||
## Note that [param from] and [param dir] are considered in local coordinates.
|
||||
## [br][br]
|
||||
## Is not affected by [member hard_stop], [member drag_outside], and [member drag_limit].
|
||||
func flick(from : Vector2, dir : Vector2) -> void:
|
||||
drag_begin.emit()
|
||||
_kill_animation()
|
||||
_end_drag_slowdown()
|
||||
|
||||
_handle_drag_angle(dir - from)
|
||||
|
||||
_on_drag_release()
|
||||
## Returns if the carousel is currening scrolling via na animation
|
||||
func is_animating() -> bool:
|
||||
return _scroll_tween.is_running()
|
||||
## Returns if the carousel is currening being dragged by player input.
|
||||
func being_dragged() -> bool:
|
||||
return _is_dragging
|
||||
## Returns the current scroll value.
|
||||
func get_scroll(with_drag : bool = false) -> int:
|
||||
if with_drag:
|
||||
return _scroll_value + _drag_scroll_value
|
||||
return _scroll_value
|
||||
## Returns the current number of items in the carousel
|
||||
func get_item_count() -> int:
|
||||
return _item_count
|
||||
|
||||
|
||||
# Virtual Functions
|
||||
|
||||
## A virtual function that is is called whenever the scroll changes.
|
||||
func _on_progress(scroll : int) -> void: pass
|
||||
## A virtual function that is is called whenever the scroll changes, for each visible item in the carousel
|
||||
func _on_item_progress(item : Control, local_scroll : int, scroll : int, local_index : int, index : int) -> void: pass
|
||||
|
||||
|
||||
|
||||
func _handle_drag_angle(local_pos : Vector2) -> void:
|
||||
var projected_scalar : float = -local_pos.dot(_angle_vec) / _angle_vec.length_squared()
|
||||
_drag_velocity = projected_scalar
|
||||
_drag_scroll_value += projected_scalar
|
||||
|
||||
if drag_limit != 0:
|
||||
_drag_scroll_value = clampi(_drag_scroll_value, -drag_limit, drag_limit)
|
||||
|
||||
if snap_behavior == SNAP_BEHAVIOR.PAGING:
|
||||
if paging_requirement < _drag_scroll_value:
|
||||
_drag_scroll_value = 0
|
||||
var desired := get_carousel_index() + 1
|
||||
if allow_loop || desired < _item_count:
|
||||
_create_animation(desired, ANIMATION_TYPE.SNAP)
|
||||
elif -paging_requirement > _drag_scroll_value:
|
||||
_drag_scroll_value = 0
|
||||
var desired := get_carousel_index() - 1
|
||||
if allow_loop || desired >= 0:
|
||||
_create_animation(desired, ANIMATION_TYPE.SNAP)
|
||||
else:
|
||||
_adjust_children()
|
||||
|
||||
|
||||
# Private Functions
|
||||
|
||||
func _get_child_rect(child : Control) -> Rect2:
|
||||
var child_pos : Vector2 = (size - item_size) * 0.5
|
||||
var child_size : Vector2
|
||||
|
||||
child_size = child.get_combined_minimum_size()
|
||||
match child.size_flags_horizontal:
|
||||
SIZE_FILL: child_size.x = item_size.x
|
||||
SIZE_SHRINK_BEGIN: pass
|
||||
SIZE_SHRINK_CENTER: child_pos.x += (item_size.x - child_size.x) * 0.5
|
||||
SIZE_SHRINK_END: child_pos.x += (item_size.x - child_size.x)
|
||||
match child.size_flags_vertical:
|
||||
SIZE_FILL: child_size.y = item_size.y
|
||||
SIZE_SHRINK_BEGIN: pass
|
||||
SIZE_SHRINK_CENTER: child_pos.y += (item_size.y - child_size.y) * 0.5
|
||||
SIZE_SHRINK_END: child_pos.y += (item_size.y - child_size.y)
|
||||
|
||||
child.size = child_size
|
||||
child.position = child_pos
|
||||
|
||||
return Rect2(child_pos, child_size)
|
||||
func _get_control_children() -> Array[Control]:
|
||||
var ret : Array[Control]
|
||||
ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible))
|
||||
return ret
|
||||
func _get_relevant_axis() -> int:
|
||||
var abs_angle_vec = _angle_vec.abs()
|
||||
|
||||
if abs_angle_vec.y >= abs_angle_vec.x:
|
||||
return (item_size.x / abs_angle_vec.y) + item_seperation
|
||||
return (item_size.y / abs_angle_vec.x) + item_seperation
|
||||
func _get_adjusted_scroll() -> int:
|
||||
var scroll := _scroll_value
|
||||
if snap_behavior != SNAP_BEHAVIOR.PAGING:
|
||||
scroll += _drag_scroll_value
|
||||
if enforce_border:
|
||||
scroll = clampi(scroll, -border_limit, _get_relevant_axis() * (_item_count - 1) + border_limit)
|
||||
elif display_loop:
|
||||
scroll = posmod(scroll, _get_relevant_axis() * _item_count)
|
||||
return scroll
|
||||
|
||||
|
||||
func _create_animation(idx : int, animation_type : ANIMATION_TYPE) -> void:
|
||||
_kill_animation()
|
||||
if _item_count == 0: return
|
||||
_scroll_tween = create_tween()
|
||||
|
||||
var axis := _get_relevant_axis()
|
||||
var max_scroll := axis * _item_count
|
||||
if max_scroll == 0: max_scroll = 1
|
||||
|
||||
var desired_scroll := posmod(axis * idx, max_scroll)
|
||||
|
||||
if allow_loop && display_loop:
|
||||
_scroll_value = posmod(_scroll_value, max_scroll)
|
||||
if abs(_scroll_value - desired_scroll) > (max_scroll >> 1):
|
||||
var left_distance := posmod(_scroll_value - desired_scroll, max_scroll)
|
||||
var right_distance := posmod(desired_scroll - _scroll_value, max_scroll)
|
||||
|
||||
if left_distance < right_distance:
|
||||
desired_scroll -= max_scroll
|
||||
else:
|
||||
desired_scroll += max_scroll
|
||||
|
||||
_last_animation = animation_type
|
||||
match animation_type:
|
||||
ANIMATION_TYPE.MANUAL:
|
||||
_scroll_tween.set_ease(manual_carousel_ease_type)
|
||||
_scroll_tween.set_trans(manual_carousel_transtion_type)
|
||||
_scroll_tween.tween_method(
|
||||
_animation_method,
|
||||
_scroll_value,
|
||||
desired_scroll,
|
||||
manual_carousel_duration)
|
||||
ANIMATION_TYPE.SNAP:
|
||||
snap_begin.emit()
|
||||
_scroll_tween.set_ease(snap_carousel_ease_type)
|
||||
_scroll_tween.set_trans(snap_carousel_transtion_type)
|
||||
_scroll_tween.tween_method(
|
||||
_animation_method,
|
||||
_scroll_value,
|
||||
desired_scroll,
|
||||
snap_carousel_duration)
|
||||
_scroll_tween.tween_callback(_kill_animation)
|
||||
_scroll_tween.play()
|
||||
func _animation_method(scroll : int) -> void:
|
||||
_scroll_value = scroll
|
||||
_adjust_children()
|
||||
func _kill_animation() -> void:
|
||||
if _last_animation == ANIMATION_TYPE.SNAP:
|
||||
snap_end.emit()
|
||||
_last_animation = ANIMATION_TYPE.NONE
|
||||
|
||||
if _scroll_tween && _scroll_tween.is_running():
|
||||
_scroll_tween.kill()
|
||||
|
||||
|
||||
func _sort_children() -> void:
|
||||
_settup_children()
|
||||
_adjust_children()
|
||||
func _settup_children() -> void:
|
||||
var children : Array[Control] = _get_control_children()
|
||||
_item_count = children.size()
|
||||
|
||||
_item_infos.resize(_item_count)
|
||||
for i : int in range(0, _item_count):
|
||||
var item_info := ItemInfo.new()
|
||||
item_info.node = children[i]
|
||||
item_info.rect = _get_child_rect(children[i])
|
||||
_item_infos[i] = item_info
|
||||
func _adjust_children() -> void:
|
||||
if _item_count == 0: return
|
||||
|
||||
var range : Array
|
||||
var max_local_offset : int
|
||||
var children : Array[Control] = _get_control_children()
|
||||
var axis := _get_relevant_axis()
|
||||
var scroll := _get_adjusted_scroll()
|
||||
|
||||
var index : int
|
||||
var local_scroll : int
|
||||
var adjustment : int
|
||||
|
||||
if axis == 0:
|
||||
index = 0
|
||||
local_scroll = 0
|
||||
adjustment = 0
|
||||
else:
|
||||
index = int(scroll / axis)
|
||||
local_scroll = fposmod(scroll, axis)
|
||||
adjustment = int(scroll < 0)
|
||||
|
||||
index += adjustment & int(local_scroll == 0)
|
||||
|
||||
_on_progress(scroll)
|
||||
|
||||
if display_loop:
|
||||
if display_range == -1:
|
||||
max_local_offset = (_item_count >> 1)
|
||||
range = range(0, _item_count)
|
||||
for i : int in range:
|
||||
_item_infos[i].loaded = false
|
||||
else:
|
||||
max_local_offset = (display_range >> 1)
|
||||
range = range(0, (display_range << 1) + 1)
|
||||
for i : int in range(0, _item_count):
|
||||
var item_info : ItemInfo = _item_infos[i]
|
||||
item_info.loaded = false
|
||||
item_info.node.visible = false
|
||||
|
||||
for item : int in range:
|
||||
var local_offset := (item >> 1) * (((item & 1) << 1) - 1) + (item & 1)
|
||||
var local_index := posmod(index + local_offset, _item_count)
|
||||
var item_info : ItemInfo = _item_infos[local_index]
|
||||
if item_info.loaded: break
|
||||
|
||||
local_offset += adjustment
|
||||
var rect : Rect2 = item_info.rect
|
||||
|
||||
rect.position += _angle_vec * (local_offset * axis - local_scroll)
|
||||
|
||||
fit_child_in_rect(item_info.node, rect)
|
||||
item_info.loaded = true
|
||||
item_info.node.visible = true
|
||||
item_info.node.z_index = max_local_offset - abs(local_offset)
|
||||
_on_item_progress(item_info.node, local_scroll, scroll, item, local_index)
|
||||
else:
|
||||
if display_range == -1:
|
||||
max_local_offset = (_item_count >> 1) + (_item_count & 1) + 1
|
||||
range = range(0, _item_count)
|
||||
else:
|
||||
max_local_offset = display_range
|
||||
range = range(max(0, index - display_range), min(_item_count, index + display_range + 1))
|
||||
for info : ItemInfo in _item_infos:
|
||||
info.node.visible = false
|
||||
|
||||
for item : int in range:
|
||||
var local_index := item - index
|
||||
var item_info : ItemInfo = _item_infos[item]
|
||||
|
||||
local_index += adjustment
|
||||
var rect : Rect2 = item_info.rect
|
||||
|
||||
rect.position += _angle_vec * (local_index * axis - local_scroll)
|
||||
|
||||
fit_child_in_rect(item_info.node, rect)
|
||||
item_info.node.visible = true
|
||||
item_info.node.z_index = max_local_offset - abs(local_index)
|
||||
_on_item_progress(item_info.node, local_scroll, scroll, item, local_index)
|
||||
|
||||
func _start_drag_slowdown() -> void:
|
||||
if is_inside_tree() && !get_tree().process_frame.is_connected(_handle_drag_slowdown):
|
||||
get_tree().process_frame.connect(_handle_drag_slowdown)
|
||||
func _end_drag_slowdown() -> void:
|
||||
if abs(_drag_velocity) < slowdown_cutoff:
|
||||
slowdown_interupted.emit()
|
||||
_drag_velocity = 0
|
||||
if snap_behavior == SNAP_BEHAVIOR.SNAP:
|
||||
_create_animation(get_carousel_index(), ANIMATION_TYPE.SNAP)
|
||||
if is_inside_tree() && get_tree().process_frame.is_connected(_handle_drag_slowdown):
|
||||
get_tree().process_frame.disconnect(_handle_drag_slowdown)
|
||||
func _handle_drag_slowdown() -> void:
|
||||
if abs(_drag_velocity) < slowdown_cutoff:
|
||||
slowdown_end.emit()
|
||||
_end_drag_slowdown()
|
||||
return
|
||||
|
||||
if _drag_velocity > 0:
|
||||
_drag_velocity = max(0, _drag_velocity - slowdown_friction)
|
||||
else:
|
||||
_drag_velocity = min(0, _drag_velocity + slowdown_friction)
|
||||
_drag_velocity *= slowdown_drag
|
||||
_scroll_value += _drag_velocity
|
||||
_adjust_children()
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
sort_children.connect(_sort_children)
|
||||
tree_exiting.connect(_end_drag_slowdown)
|
||||
mouse_exited.connect(_mouse_check)
|
||||
|
||||
_angle_vec = Vector2.RIGHT.rotated(deg_to_rad(carousel_angle))
|
||||
func _ready() -> void:
|
||||
_settup_children()
|
||||
if _item_count > 0:
|
||||
starting_index = posmod(starting_index, _item_count)
|
||||
go_to_index(-starting_index, false)
|
||||
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name == "enforce_border":
|
||||
if display_loop:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name == "border_limit":
|
||||
if display_loop || !enforce_border:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name == "paging_requirement":
|
||||
if snap_behavior != SNAP_BEHAVIOR.PAGING:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name == "hard_stop":
|
||||
if snap_behavior == SNAP_BEHAVIOR.PAGING:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name in ["slowdown_drag", "slowdown_friction", "slowdown_cutoff"]:
|
||||
if hard_stop || snap_behavior == SNAP_BEHAVIOR.PAGING:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name in ["drag_outside"]:
|
||||
if !can_drag:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is not InputEventMouse:
|
||||
return
|
||||
|
||||
var has_point := get_viewport_rect().has_point(event.position)
|
||||
|
||||
if (
|
||||
(event is InputEventScreenDrag || event is InputEventMouseMotion) &&
|
||||
(drag_outside || has_point)
|
||||
):
|
||||
if event.pressure == 0:
|
||||
if _is_dragging: _on_drag_release()
|
||||
return
|
||||
|
||||
if !_is_dragging && has_point:
|
||||
drag_begin.emit()
|
||||
_mouse_checking = true
|
||||
_end_drag_slowdown()
|
||||
_kill_animation()
|
||||
_is_dragging = true
|
||||
|
||||
_handle_drag_angle(event.relative)
|
||||
elif (event is InputEventScreenTouch || event is InputEventMouseButton):
|
||||
if !event.pressed: _on_drag_release()
|
||||
func _on_drag_release() -> void:
|
||||
_mouse_checking = false
|
||||
_is_dragging = false
|
||||
drag_end.emit()
|
||||
|
||||
if snap_behavior != SNAP_BEHAVIOR.PAGING:
|
||||
_scroll_value = _get_adjusted_scroll()
|
||||
_drag_scroll_value = 0
|
||||
if snap_behavior == SNAP_BEHAVIOR.NONE:
|
||||
if !hard_stop: _start_drag_slowdown()
|
||||
elif snap_behavior == SNAP_BEHAVIOR.SNAP:
|
||||
if hard_stop: _create_animation(get_carousel_index(), ANIMATION_TYPE.SNAP)
|
||||
else: _start_drag_slowdown()
|
||||
|
||||
func _mouse_check() -> void:
|
||||
if _mouse_checking:
|
||||
_on_drag_release()
|
||||
|
||||
|
||||
func _get_allowed_size_flags_horizontal() -> PackedInt32Array:
|
||||
return [SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END]
|
||||
func _get_allowed_size_flags_vertical() -> PackedInt32Array:
|
||||
return [SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END]
|
||||
|
||||
|
||||
# Used to hold data about a carousel item
|
||||
class ItemInfo:
|
||||
var node : Control
|
||||
var rect : Rect2
|
||||
var loaded : bool
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,277 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name CircularContainer extends Container
|
||||
## A container that positions children in a ellipse within the bounds of this node.
|
||||
|
||||
|
||||
|
||||
## Behavior the auto angle setter will exhibit.
|
||||
enum BOUND_BEHAVIOR {
|
||||
NONE, ## No end bound for angles
|
||||
STOP, ## Angles, if exceeding the max, will be hard stopped at the max.
|
||||
LOOP, ## Angles, if exceeding the max, will loop back to the begining.
|
||||
MIRRIOR ## Angles, if exceeding the max, will bounce back and forth between the min and max angles.
|
||||
}
|
||||
|
||||
|
||||
## The horizontal offset of the ellipse's center.
|
||||
## [br][br]
|
||||
## [code]0[/code] is fully left and [code]1[/code] is fully right.
|
||||
@export_range(0, 1) var origin_x : float = 0.5:
|
||||
set(val):
|
||||
origin_x = val
|
||||
queue_sort()
|
||||
## The vertical offset of the ellipse's center.
|
||||
## [br][br]
|
||||
## [code]0[/code] is fully top and [code]1[/code] is fully bottom.
|
||||
@export_range(0, 1) var origin_y : float = 0.5:
|
||||
set(val):
|
||||
origin_y = val
|
||||
queue_sort()
|
||||
|
||||
## The horizontal radius of the ellipse's center.
|
||||
## [br][br]
|
||||
## [code]0[/code] is [code]0[/code], [code]0.5[/code] is half of [member Control.size].x, and [code]1[/code] is [member Control.size].x.
|
||||
@export_range(0, 1) var xRadius : float = 0.5:
|
||||
set(val):
|
||||
xRadius = val
|
||||
queue_sort()
|
||||
## The vertical radius of the ellipse's center.
|
||||
## [br][br]
|
||||
## [code]0[/code] is [code]0[/code], [code]0.5[/code] is half of [member Control.size].y, and [code]1[/code] is [member Control.size].y.
|
||||
@export_range(0, 1) var yRadius : float = 0.5:
|
||||
set(val):
|
||||
yRadius = val
|
||||
queue_sort()
|
||||
|
||||
@export_group("Angles")
|
||||
## If [code]false[/code], the node will automatically write the angles of children.[br]
|
||||
## If [code]true[/code], you will be required to manually input angles for each child.
|
||||
@export var manual : bool = false:
|
||||
set(val):
|
||||
if manual != val:
|
||||
manual = val
|
||||
notify_property_list_changed()
|
||||
_calculate_angles()
|
||||
|
||||
## The behavior this node will have if the angle exceeds the max set angle in auto-mode.
|
||||
## [br][br]
|
||||
## See [member manual] and [member angle_end],
|
||||
var bound_behavior : BOUND_BEHAVIOR:
|
||||
set(val):
|
||||
if bound_behavior != val:
|
||||
bound_behavior = val
|
||||
notify_property_list_changed()
|
||||
_calculate_angles()
|
||||
## If true, the nodes will be equal-distantantly placed from each on, between the start and end angles. Overides all other [member bound_behavior].
|
||||
## [br][br]
|
||||
## See [member manual], [member bound_behavior], [member angle_start], and [member angle_end].
|
||||
var equal_distant : bool:
|
||||
set(val):
|
||||
if equal_distant != val:
|
||||
equal_distant = val
|
||||
notify_property_list_changed()
|
||||
_calculate_angles()
|
||||
|
||||
## The angle auto-mode will increment from.
|
||||
## [br][br]
|
||||
## See [member manual], [member bound_behavior], [member angle_start], and [member angle_end].
|
||||
var angle_start : float = 0:
|
||||
set(val):
|
||||
if angle_start != val:
|
||||
angle_start = val
|
||||
_calculate_angles()
|
||||
## The angle auto-mode will increment with.
|
||||
## [br][br]
|
||||
## See [member manual].
|
||||
var angle_step : float = 10:
|
||||
set(val):
|
||||
if angle_step != val:
|
||||
angle_step = val
|
||||
_calculate_angles()
|
||||
## The angle auto-mode will increment to.
|
||||
## [br][br]
|
||||
## See [member manual] and [member bound_behavior].
|
||||
var angle_end : float = 360:
|
||||
set(val):
|
||||
if angle_end != val:
|
||||
angle_end = val
|
||||
_calculate_angles()
|
||||
|
||||
@export_storage var _container_angles : PackedFloat32Array
|
||||
## The list of angles this ndoe uses to position each child, within the order they are positions in the tree.
|
||||
var angles : PackedFloat32Array:
|
||||
set(val):
|
||||
_container_angles.resize(max(_get_control_children().size(), val.size()))
|
||||
|
||||
for i in range(0, val.size()):
|
||||
_container_angles[i] = deg_to_rad(val[i])
|
||||
for i in range(val.size(), _container_angles.size()):
|
||||
_container_angles[i] = 0
|
||||
|
||||
_fix_childrend()
|
||||
get:
|
||||
var ret : PackedFloat32Array
|
||||
ret.resize(_container_angles.size())
|
||||
|
||||
for i in range(0, _container_angles.size()):
|
||||
ret[i] = rad_to_deg(_container_angles[i])
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
sort_children.connect(_fix_childrend)
|
||||
child_order_changed.connect(_childrend_changed)
|
||||
func _ready() -> void:
|
||||
_fix_childrend()
|
||||
|
||||
func _childrend_changed() -> void:
|
||||
_calculate_angles()
|
||||
_fix_childrend()
|
||||
func _get_control_children() -> Array[Control]:
|
||||
var ret : Array[Control]
|
||||
ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible))
|
||||
return ret
|
||||
func _fix_childrend() -> void:
|
||||
var children := _get_control_children()
|
||||
|
||||
for index : int in range(0, children.size()):
|
||||
var child : Control = children[index]
|
||||
if child: _fix_child(child, index)
|
||||
func _fix_child(child : Control, index : int) -> void:
|
||||
if _container_angles.is_empty(): return
|
||||
child.reset_size()
|
||||
|
||||
# Calculates child position
|
||||
var child_size := child.get_combined_minimum_size()
|
||||
var child_pos := -(child_size * 0.5) + (Vector2(origin_x, origin_y) + (Vector2(xRadius, yRadius) * Vector2(cos(_container_angles[index]), sin(_container_angles[index])))) * size
|
||||
|
||||
# Keeps children in the rect's top-left boundards
|
||||
if child_pos.x < 0:
|
||||
child_pos.x = 0
|
||||
if child_pos.y < 0:
|
||||
child_pos.y = 0
|
||||
|
||||
# Keeps children in the rect's bottom-right boundards
|
||||
if child_pos.x + child_size.x > size.x:
|
||||
child_pos.x += size.x - (child_pos.x + child_size.x)
|
||||
if child_pos.y + child_size.y > size.y:
|
||||
child_pos.y += size.y - (child_pos.y + child_size.y)
|
||||
if child_pos.y + child_size.y > size.y:
|
||||
child_size.y = size.y - child_pos.y
|
||||
|
||||
fit_child_in_rect(child, Rect2(child_pos, child_size))
|
||||
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
var properties : Array[Dictionary]
|
||||
|
||||
if manual:
|
||||
properties.append({
|
||||
"name": "angles",
|
||||
"type": TYPE_PACKED_FLOAT32_ARRAY,
|
||||
"usage" : PROPERTY_USAGE_EDITOR
|
||||
})
|
||||
else:
|
||||
var unbounded = PROPERTY_USAGE_READ_ONLY if bound_behavior == BOUND_BEHAVIOR.NONE else 0
|
||||
var allow_step = PROPERTY_USAGE_READ_ONLY if equal_distant && bound_behavior == BOUND_BEHAVIOR.STOP else 0
|
||||
|
||||
properties.append({
|
||||
"name": "bound_behavior",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": ",".join(BOUND_BEHAVIOR.keys()),
|
||||
"usage" : PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
properties.append({
|
||||
"name": "equal_distant",
|
||||
"type": TYPE_BOOL,
|
||||
"usage" : PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
properties.append({
|
||||
"name": "Values",
|
||||
"type": TYPE_NIL,
|
||||
"usage" : PROPERTY_USAGE_SUBGROUP,
|
||||
"hint_string": ""
|
||||
})
|
||||
properties.append({
|
||||
"name": "angle_start",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage" : PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
properties.append({
|
||||
"name": "angle_step",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage" : PROPERTY_USAGE_DEFAULT | allow_step
|
||||
})
|
||||
properties.append({
|
||||
"name": "angle_end",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage" : PROPERTY_USAGE_DEFAULT | unbounded
|
||||
})
|
||||
|
||||
return properties
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
match property:
|
||||
"bound_behavior":
|
||||
return bound_behavior != BOUND_BEHAVIOR.NONE
|
||||
"equal_distant":
|
||||
return equal_distant
|
||||
"angle_start":
|
||||
return angle_start != 0
|
||||
"angle_step":
|
||||
return angle_step != 10
|
||||
"angle_end":
|
||||
return angle_end != 360
|
||||
|
||||
return false
|
||||
func _property_get_revert(property: StringName) -> Variant:
|
||||
match property:
|
||||
"bound_behavior":
|
||||
return BOUND_BEHAVIOR.NONE
|
||||
"equal_distant":
|
||||
return false
|
||||
"angle_start":
|
||||
return 0
|
||||
"angle_step":
|
||||
return 10
|
||||
"angle_end":
|
||||
return 360
|
||||
|
||||
return null
|
||||
|
||||
func _calculate_angles() -> void:
|
||||
if manual: return
|
||||
|
||||
var count := _get_control_children().size()
|
||||
_container_angles.resize(count)
|
||||
|
||||
var start := deg_to_rad(angle_start)
|
||||
var end := deg_to_rad(angle_end)
|
||||
|
||||
var step : float
|
||||
if equal_distant:
|
||||
if count != 0: step = deg_to_rad((angle_end - angle_start) / (count - 1))
|
||||
else:
|
||||
step = deg_to_rad(angle_step)
|
||||
|
||||
var inc_func : Callable
|
||||
if equal_distant || bound_behavior == BOUND_BEHAVIOR.NONE:
|
||||
inc_func = func(i : int): return (i * step) + start
|
||||
elif bound_behavior == BOUND_BEHAVIOR.STOP:
|
||||
inc_func = func(i : int): return min(i * step, end) + start
|
||||
elif bound_behavior == BOUND_BEHAVIOR.LOOP:
|
||||
inc_func = func(i : int): return fmod(i * step, end) + start
|
||||
elif bound_behavior == BOUND_BEHAVIOR.MIRRIOR:
|
||||
inc_func = func(i : int): return abs(fmod((i * step) - end, 2 * end) - end) + start
|
||||
|
||||
for i : int in range(0, _container_angles.size()):
|
||||
_container_angles[i] = fposmod(inc_func.call(i), TAU)
|
||||
_fix_childrend()
|
||||
|
||||
func _get_allowed_size_flags_horizontal() -> PackedInt32Array:
|
||||
return []
|
||||
func _get_allowed_size_flags_vertical() -> PackedInt32Array:
|
||||
return []
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
798
godot/addons/FreeControl/src/CustomClasses/Drawer/Drawer.gd
Normal file
|
@ -0,0 +1,798 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name Drawer extends Container
|
||||
## A [Container] node used for easy UI Drawers.
|
||||
|
||||
## A flag enum used to classify which input type is allowed.
|
||||
enum ActionMode {
|
||||
ACTION_MODE_BUTTON_NONE = 0, ## Allows no input
|
||||
ACTION_MODE_BUTTON_PRESS = 1, ## Toggles the drawer on tap/click press
|
||||
ACTION_MODE_BUTTON_RELEASE = 2, ## Toggles the drawer on tap/click release
|
||||
ACTION_MODE_BUTTON_DRAG = 4, ## Allows user to drag the drawer
|
||||
}
|
||||
|
||||
## An enum used to classify where input is accepted.
|
||||
enum InputAreaMode {
|
||||
Nowhere = 0, ## No input is alloed anywhere on the screen.
|
||||
Anywhere = 1, ## Input accepted anywere on the screen.
|
||||
WithinBounds = 2, ## Input is accepted only within this node's rect.
|
||||
ExcludeDrawer = 3, ## Input is accepted anywhere except on the drawer's rect.
|
||||
WithinEmptyBounds = 4, ## Input is accepted only within this node's rect, outside of the drawer's rect.
|
||||
}
|
||||
|
||||
## An enum used to classify when dragging is allowed.
|
||||
enum DragMode {
|
||||
NEVER = 0, ## No dragging allowed.
|
||||
ON_OPEN = 1, ## Dragging is allowed to open the drawer.
|
||||
ON_CLOSE = 2, ## Dragging is allowed to close the drawer.
|
||||
ON_OPEN_OR_CLOSE = 0b11 ## Dragging is allowed to open or close the drawer.
|
||||
}
|
||||
|
||||
|
||||
## Emited when drawer is begining an opening/closing animation.
|
||||
## [br][br]
|
||||
## Also see: [member state], [method toggle_drawer].
|
||||
signal slide_begin
|
||||
## Emited when drawer is ending an opening/closing animation.
|
||||
## [br][br]
|
||||
## Also see: [member state], [method toggle_drawer].
|
||||
signal slide_end
|
||||
## Emited when state has changed, but animation has not began.
|
||||
## [br][br]
|
||||
## Also see: [member state], [method toggle_drawer].
|
||||
signal state_toggle_begin(toggle : bool)
|
||||
## Emited when state has changed and animation has finished.
|
||||
## [br][br]
|
||||
## Also see: [member state], [method toggle_drawer].
|
||||
signal state_toggle_end(toggle : bool)
|
||||
## Emited when drag has began.
|
||||
## [br][br]
|
||||
## Also see: [member allow_drag].
|
||||
signal drag_start
|
||||
## Emited when drag has ended.
|
||||
## [br][br]
|
||||
## Also see: [member allow_drag].
|
||||
signal drag_end
|
||||
|
||||
|
||||
|
||||
@export_storage var _state : bool
|
||||
## The state of the drawer. If [code]true[/code], the drawer is open. Otherwise closed.
|
||||
## [br][br]
|
||||
## Also see: [method toggle_drawer].
|
||||
var state : bool:
|
||||
get: return _state
|
||||
set(val):
|
||||
if _state != val:
|
||||
_toggle_drawer(val)
|
||||
|
||||
#@export_group("Drawer Angle")
|
||||
## The angle in which the drawer will open/close from.
|
||||
## [br][br]
|
||||
## Also see: [member drawer_angle_axis_snap].
|
||||
var drawer_angle : float = 0.0:
|
||||
set(val):
|
||||
if drawer_angle != val:
|
||||
drawer_angle = val
|
||||
_angle_vec = Vector2.RIGHT.rotated(deg_to_rad(drawer_angle))
|
||||
|
||||
_kill_animation()
|
||||
_calculate_childrend()
|
||||
## If [code]true[/code], the drawer will be snapped to move as strictly cardinally as possible.
|
||||
## [br][br]
|
||||
## Also see: [member drawer_angle].
|
||||
var drawer_angle_axis_snap : bool:
|
||||
set(val):
|
||||
if drawer_angle_axis_snap != val:
|
||||
drawer_angle_axis_snap = val
|
||||
|
||||
_kill_animation()
|
||||
_calculate_childrend()
|
||||
|
||||
#@export_group("Drawer Span")
|
||||
## If [code]false[/code], [member drawer_width] is equal to a ratio of this node's [Control.size]'s x component.
|
||||
## [br]. Else, [member drawer_width] is directly editable.
|
||||
var drawer_width_by_pixel : bool:
|
||||
set(val):
|
||||
if val != drawer_width_by_pixel:
|
||||
drawer_width_by_pixel = val
|
||||
if val:
|
||||
drawer_width *= size.x
|
||||
else:
|
||||
if size.x == 0:
|
||||
drawer_width = 0
|
||||
else:
|
||||
drawer_width /= size.x
|
||||
|
||||
notify_property_list_changed()
|
||||
## The width of the drawer.
|
||||
## [br][br]
|
||||
## Also see: [member drawer_width_by_pixel].
|
||||
var drawer_width : float = 1:
|
||||
set(val):
|
||||
if val != drawer_width:
|
||||
drawer_width = val
|
||||
_calculate_childrend()
|
||||
## If [code]false[/code], [member drawer_height] is equal to a ratio of this node's [Control.size]'s y component.
|
||||
## [br]. Else, [member drawer_height] is directly editable.
|
||||
var drawer_height_by_pixel : bool:
|
||||
set(val):
|
||||
if val != drawer_height_by_pixel:
|
||||
drawer_height_by_pixel = val
|
||||
if val:
|
||||
drawer_height *= size.y
|
||||
else:
|
||||
if size.y == 0:
|
||||
drawer_height = 0
|
||||
else:
|
||||
drawer_height /= size.y
|
||||
|
||||
notify_property_list_changed()
|
||||
## The height of the drawer.
|
||||
## [br][br]
|
||||
## Also see: [member drawer_height_by_pixel].
|
||||
var drawer_height : float = 1:
|
||||
set(val):
|
||||
if val != drawer_height:
|
||||
drawer_height = val
|
||||
_calculate_childrend()
|
||||
|
||||
#@export_group("Input Options")
|
||||
## A flag enum used to classify which input type is allowed.
|
||||
var action_mode : ActionMode = ActionMode.ACTION_MODE_BUTTON_PRESS:
|
||||
set(val):
|
||||
if val != action_mode:
|
||||
action_mode = val
|
||||
_is_dragging = false
|
||||
|
||||
#@export_subgroup("Margins")
|
||||
## Extra pixels to where the open drawer lies when open.
|
||||
var open_margin : int = 0:
|
||||
set(val):
|
||||
if val != open_margin:
|
||||
open_margin = val
|
||||
_calculate_childrend()
|
||||
## Extra pixels to where the open drawer lies when closed.
|
||||
var close_margin : int = 0:
|
||||
set(val):
|
||||
if val != close_margin:
|
||||
close_margin = val
|
||||
_calculate_childrend()
|
||||
|
||||
#@export_subgroup("Drag Options")
|
||||
## Permissions on how the user may drag to open/close the drawer.
|
||||
## [br][br]
|
||||
## Also see: [member allow_drag], [member smooth_drag].
|
||||
var allow_drag : DragMode = DragMode.ON_OPEN_OR_CLOSE
|
||||
## If [code]true[/code], the drawer will react while the user drags.
|
||||
var smooth_drag : bool = true
|
||||
## The amount of extra the user is allowed to drag (in the open direction) before being stopped.
|
||||
var drag_give : int = 0
|
||||
|
||||
#@export_subgroup("Open Input")
|
||||
## A node to determine where vaild input, when closed, may start at.
|
||||
## [br][br]
|
||||
## Also see: [member allow_drag].
|
||||
var open_bounds : InputAreaMode = InputAreaMode.WithinEmptyBounds
|
||||
## The minimum amount you need to drag before your drag is considered to have closed the drawer.
|
||||
## [br][br]
|
||||
## Also see: [member allow_drag].
|
||||
var open_drag_threshold : int = 50:
|
||||
set(val):
|
||||
val = max(0, val)
|
||||
if val != open_drag_threshold:
|
||||
open_drag_threshold = val
|
||||
|
||||
#@export_subgroup("Close Input")
|
||||
## A node to determine where vaild input, when open, may start at.
|
||||
## [br][br]
|
||||
## Also see: [member allow_drag].
|
||||
var close_bounds : InputAreaMode = InputAreaMode.WithinEmptyBounds
|
||||
## The minimum amount you need to drag before your drag is considered to have opened the drawer.
|
||||
## [br][br]
|
||||
## Also see: [member allow_drag].
|
||||
var close_drag_threshold : int = 50:
|
||||
set(val):
|
||||
val = max(0, val)
|
||||
if val != close_drag_threshold:
|
||||
close_drag_threshold = val
|
||||
|
||||
#@export_group("Animation")
|
||||
#@export_subgroup("Manual Animation")
|
||||
## The [enum Tween.TransitionType] used when manually opening and closing drawer.
|
||||
## [br][br]
|
||||
## Also see: [member state], [method toggle_drawer].
|
||||
var manual_drawer_translate : Tween.TransitionType
|
||||
## The [enum Tween.EaseType] used when manually opening and closing drawer.
|
||||
## [br][br]
|
||||
## Also see: [member state], [method toggle_drawer].
|
||||
var manual_drawer_ease : Tween.EaseType
|
||||
## The animation duration used when manually opening and closing drawer.
|
||||
## [br][br]
|
||||
## Also see: [member state], [method toggle_drawer].
|
||||
var manual_drawer_duration : float = 0.2
|
||||
|
||||
#@export_subgroup("Drag Animation")
|
||||
## The [enum Tween.TransitionType] used when snapping after a drag.
|
||||
var drag_drawer_translate : Tween.TransitionType
|
||||
## The [enum Tween.EaseType] used when snapping after a drag.
|
||||
var drag_drawer_ease : Tween.EaseType
|
||||
## The animation duration used when snapping after a drag.
|
||||
var drag_drawer_duration : float = 0.2
|
||||
|
||||
|
||||
|
||||
var _min_size : Vector2
|
||||
var _angle_vec : Vector2
|
||||
|
||||
var _animation_tween : Tween
|
||||
var _current_progress : float
|
||||
var _drag_value : float
|
||||
var _is_dragging : bool
|
||||
var _has_dragged : bool
|
||||
|
||||
var _inner_offset : Vector2
|
||||
var _outer_offset : Vector2
|
||||
var _max_offset : float
|
||||
|
||||
|
||||
## Returns if the drawer is currently open.
|
||||
func is_open() -> bool:
|
||||
return get_progress_adjusted() > 0.5
|
||||
## Returns if the drawer is expected to be open.
|
||||
func is_open_expected() -> bool:
|
||||
return _state
|
||||
## Returns if the drawer is currently animating.
|
||||
func is_animating() -> bool:
|
||||
return _animation_tween && _animation_tween.is_running()
|
||||
## Returns the size of the drawer.
|
||||
func get_drawer_size() -> Vector2:
|
||||
var ret := Vector2(drawer_width, drawer_height)
|
||||
if !drawer_width_by_pixel:
|
||||
ret.x *= size.x
|
||||
if !drawer_height_by_pixel:
|
||||
ret.y *= size.y
|
||||
return ret.max(_min_size)
|
||||
## Returns the offsert the drawer has, compared to this node's local position.
|
||||
func get_drawer_offset(with_drag : bool = false) -> Vector2:
|
||||
return _get_drawer_offset(_inner_offset, _outer_offset, with_drag)
|
||||
## Returns the rect the drawer has, compared to this node's local position.
|
||||
func get_drawer_rect(with_drag : bool = false) -> Rect2:
|
||||
return Rect2(get_drawer_offset(with_drag), get_drawer_size())
|
||||
|
||||
## Gets the current progress the drawer is in animations.
|
||||
## Returns the value in pixel distance.
|
||||
func get_progress(include_drag : bool = false, with_clamp : bool = true) -> float:
|
||||
var ret : float = _current_progress
|
||||
if include_drag:
|
||||
ret += _drag_value
|
||||
if with_clamp:
|
||||
ret = clampf(ret, -drag_give, _max_offset)
|
||||
return ret
|
||||
## Gets the percentage of the drawer's current position between being closed and opened.
|
||||
## [code]0.0[/code] when closed and [code]1.0[/code] when opened.
|
||||
func get_progress_adjusted(include_drag : bool = false, with_clamp : bool = true) -> float:
|
||||
if _max_offset == 0.0: return 0.0
|
||||
return get_progress(include_drag, with_clamp) / _max_offset
|
||||
|
||||
|
||||
|
||||
func _get_relevant_axis() -> float:
|
||||
var drawer_size := get_drawer_size()
|
||||
var abs_angle_vec = _angle_vec.abs()
|
||||
|
||||
if abs_angle_vec.y >= abs_angle_vec.x:
|
||||
return (drawer_size.x / abs_angle_vec.y)
|
||||
return (drawer_size.y / abs_angle_vec.x)
|
||||
func _get_control_children() -> Array[Control]:
|
||||
var ret : Array[Control]
|
||||
ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible))
|
||||
return ret
|
||||
func _calculate_childrend() -> void:
|
||||
_find_offsets()
|
||||
_current_progress = _max_offset * float(_state)
|
||||
_adjust_children()
|
||||
func _adjust_children() -> void:
|
||||
var rect := get_drawer_rect(true)
|
||||
for child : Control in _get_control_children():
|
||||
fit_child_in_rect(child, rect)
|
||||
|
||||
|
||||
|
||||
func _find_minimum_size() -> Vector2:
|
||||
var min_size : Vector2 = Vector2.ZERO
|
||||
for child : Control in _get_control_children():
|
||||
min_size = min_size.max(child.get_combined_minimum_size())
|
||||
return min_size
|
||||
func _find_offsets() -> void:
|
||||
var drawer_size := get_drawer_size()
|
||||
|
||||
var distances_to_intersection_point := (size / _angle_vec).abs()
|
||||
var inner_distance := minf(distances_to_intersection_point.x, distances_to_intersection_point.y)
|
||||
var inner_point : Vector2 = (inner_distance * _angle_vec + (size - drawer_size)) * 0.5
|
||||
_inner_offset = inner_point.maxf(0).min(size - drawer_size)
|
||||
|
||||
if drawer_angle_axis_snap:
|
||||
var half_drawer_size := drawer_size * 0.5
|
||||
var inner_point_half := inner_point + half_drawer_size
|
||||
_outer_offset = inner_point
|
||||
|
||||
if abs(inner_point_half.x - size.x) < 0.01:
|
||||
_outer_offset.x += half_drawer_size.x
|
||||
elif abs(inner_point_half.x) < 0.01:
|
||||
_outer_offset.x -= half_drawer_size.x
|
||||
|
||||
if abs(inner_point_half.y - size.y) < 0.01:
|
||||
_outer_offset.y += half_drawer_size.y
|
||||
elif abs(inner_point_half.y) < 0.01:
|
||||
_outer_offset.y -= half_drawer_size.y
|
||||
else:
|
||||
var distances_to_outer_center := ((size + drawer_size) / _angle_vec).abs()
|
||||
var outer_distance := minf(distances_to_outer_center.x, distances_to_outer_center.y)
|
||||
_outer_offset = (outer_distance * _angle_vec + (size - drawer_size)) * 0.5
|
||||
|
||||
_max_offset = (_outer_offset - _inner_offset).length()
|
||||
_inner_offset = (_inner_offset + _angle_vec * open_margin).floor()
|
||||
_outer_offset = (_outer_offset - _angle_vec * close_margin).floor()
|
||||
|
||||
|
||||
## Allows opening and closing the drawer.
|
||||
## [br][br]
|
||||
## Also see: [member state] and [method force_drawer].
|
||||
func toggle_drawer(open : bool) -> void:
|
||||
_toggle_drawer(open)
|
||||
func _toggle_drawer(open : bool, drag_animate : bool = false) -> void:
|
||||
slide_begin.emit()
|
||||
_animate_to_progress(float(open), drag_animate)
|
||||
_animation_tween.tween_callback(slide_end.emit)
|
||||
|
||||
if _state != open:
|
||||
state_toggle_begin.emit(open)
|
||||
_animation_tween.tween_callback(state_toggle_end.emit.bind(open))
|
||||
_state = open
|
||||
func _animate_to_progress(
|
||||
to_progress : float,
|
||||
drag_animate : bool = false
|
||||
) -> void:
|
||||
_kill_animation()
|
||||
_animation_tween = create_tween()
|
||||
|
||||
if drag_animate:
|
||||
_animation_tween.set_trans(drag_drawer_translate)
|
||||
_animation_tween.set_ease(drag_drawer_ease)
|
||||
_animation_tween.tween_method(
|
||||
_animation_method,
|
||||
get_progress(true),
|
||||
to_progress * _max_offset,
|
||||
drag_drawer_duration
|
||||
)
|
||||
else:
|
||||
_animation_tween.set_trans(manual_drawer_translate)
|
||||
_animation_tween.set_ease(manual_drawer_ease)
|
||||
_animation_tween.tween_method(
|
||||
_animation_method,
|
||||
get_progress(true),
|
||||
to_progress * _max_offset,
|
||||
manual_drawer_duration
|
||||
)
|
||||
func _kill_animation() -> void:
|
||||
if _animation_tween && _animation_tween.is_running():
|
||||
_animation_tween.kill()
|
||||
func _animation_method(progress : float) -> void:
|
||||
_current_progress = progress
|
||||
_progress_changed(get_progress_adjusted())
|
||||
_adjust_children()
|
||||
|
||||
|
||||
|
||||
## Allows opening and closing the drawer without animation.
|
||||
## [br][br]
|
||||
## Also see: [member state] and [method toggle_drawer].
|
||||
func force_drawer(open : bool) -> void:
|
||||
_kill_animation()
|
||||
_state = open
|
||||
_animation_method(float(open) * _max_offset)
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
resized.connect(_calculate_childrend)
|
||||
sort_children.connect(_calculate_childrend)
|
||||
|
||||
_angle_vec = Vector2.RIGHT.rotated(deg_to_rad(drawer_angle))
|
||||
func _get_minimum_size() -> Vector2:
|
||||
_min_size = _find_minimum_size()
|
||||
return _min_size
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
var ret : Array[Dictionary]
|
||||
|
||||
ret.append({
|
||||
"name": "state",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_EDITOR
|
||||
})
|
||||
|
||||
|
||||
ret.append({
|
||||
"name": "Drawer Angle",
|
||||
"type": TYPE_NIL,
|
||||
"hint_string": "drawer_",
|
||||
"usage": PROPERTY_USAGE_GROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "drawer_angle",
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 360, 0.001, or_less, or_greater",
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "drawer_angle_axis_snap",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
|
||||
ret.append({
|
||||
"name": "Drawer Span",
|
||||
"type": TYPE_NIL,
|
||||
"hint_string": "drawer_",
|
||||
"usage": PROPERTY_USAGE_GROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "drawer_width_by_pixel",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "drawer_width",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
}.merged({} if drawer_width_by_pixel else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 0.001, or_less, or_greater",
|
||||
}))
|
||||
ret.append({
|
||||
"name": "drawer_height_by_pixel",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "drawer_height",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
}.merged({} if drawer_height_by_pixel else {
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 0.001, or_less, or_greater",
|
||||
}))
|
||||
|
||||
ret.append({
|
||||
"name": "Input Options",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_GROUP
|
||||
})
|
||||
|
||||
ret.append({
|
||||
"name": "action_mode",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_FLAGS,
|
||||
"hint_string": "Press Action:1,Release Action:2,Drag Action:4",
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
ret.append({
|
||||
"name": "Margins",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_SUBGROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "open_margin",
|
||||
"type": TYPE_INT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "close_margin",
|
||||
"type": TYPE_INT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
ret.append({
|
||||
"name": "Drag Options",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_SUBGROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "allow_drag",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": _convert_to_enum(DragMode.keys()),
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "smooth_drag",
|
||||
"type": TYPE_BOOL,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "drag_give",
|
||||
"type": TYPE_INT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
ret.append({
|
||||
"name": "Open Input",
|
||||
"type": TYPE_NIL,
|
||||
"hint_string": "",
|
||||
"usage": PROPERTY_USAGE_SUBGROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "open_bounds",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": _convert_to_enum(InputAreaMode.keys()),
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "open_drag_threshold",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 1, or_greater",
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
ret.append({
|
||||
"name": "Close Input",
|
||||
"type": TYPE_NIL,
|
||||
"hint_string": "",
|
||||
"usage": PROPERTY_USAGE_SUBGROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "close_bounds",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": _convert_to_enum(InputAreaMode.keys()),
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "close_drag_threshold",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 1, or_greater",
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
|
||||
ret.append({
|
||||
"name": "Animation",
|
||||
"type": TYPE_NIL,
|
||||
"usage": PROPERTY_USAGE_GROUP
|
||||
})
|
||||
|
||||
ret.append({
|
||||
"name": "Manual Animation",
|
||||
"type": TYPE_NIL,
|
||||
"hint_string": "manual_drawer_",
|
||||
"usage": PROPERTY_USAGE_SUBGROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "manual_drawer_translate",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": _get_enum_string("Tween", "TransitionType"),
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "manual_drawer_ease",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": _get_enum_string("Tween", "EaseType"),
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "manual_drawer_duration",
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 0.001, or_greater",
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
ret.append({
|
||||
"name": "Drag Animation",
|
||||
"type": TYPE_NIL,
|
||||
"hint_string": "drag_drawer_",
|
||||
"usage": PROPERTY_USAGE_SUBGROUP
|
||||
})
|
||||
ret.append({
|
||||
"name": "drag_drawer_translate",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": _get_enum_string("Tween", "TransitionType"),
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "drag_drawer_ease",
|
||||
"type": TYPE_INT,
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": _get_enum_string("Tween", "EaseType"),
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
ret.append({
|
||||
"name": "drag_drawer_duration",
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0, 1, 0.001, or_greater",
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
return ret
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
return property in [
|
||||
"state",
|
||||
"drawer_angle",
|
||||
"drawer_angle_axis_snap",
|
||||
"drawer_width_by_pixel",
|
||||
"drawer_width",
|
||||
"drawer_height_by_pixel",
|
||||
"drawer_height",
|
||||
"action_mode",
|
||||
"drag_give",
|
||||
"open_margin",
|
||||
"close_margin",
|
||||
"allow_drag",
|
||||
"smooth_drag",
|
||||
"open_bounds",
|
||||
"open_drag_threshold",
|
||||
"close_bounds",
|
||||
"close_drag_threshold",
|
||||
"manual_drawer_translate",
|
||||
"manual_drawer_ease",
|
||||
"manual_drawer_duration",
|
||||
"drag_drawer_translate",
|
||||
"drag_drawer_ease",
|
||||
"drag_drawer_duration"
|
||||
]
|
||||
func _property_get_revert(property: StringName) -> Variant:
|
||||
match property:
|
||||
"smooth_drag":
|
||||
return true
|
||||
"state", "drawer_width_by_pixel", "drawer_height_by_pixel", "drawer_angle_axis_snap":
|
||||
return false
|
||||
|
||||
"drag_give", "open_margin", "close_margin":
|
||||
return 0
|
||||
"drawer_angle":
|
||||
return 0.0
|
||||
"manual_drawer_duration", "drag_drawer_duration":
|
||||
return 0.2
|
||||
"open_drag_threshold":
|
||||
return 50
|
||||
"close_drag_threshold":
|
||||
return 50
|
||||
|
||||
"drawer_width":
|
||||
return size.x if drawer_width_by_pixel else 1.0
|
||||
"drawer_height":
|
||||
return size.y if drawer_height_by_pixel else 1.0
|
||||
|
||||
"action_mode":
|
||||
return ActionMode.ACTION_MODE_BUTTON_PRESS
|
||||
"allow_drag":
|
||||
return DragMode.ON_OPEN_OR_CLOSE
|
||||
"open_bounds":
|
||||
return InputAreaMode.WithinEmptyBounds
|
||||
"close_bounds":
|
||||
return InputAreaMode.WithinEmptyBounds
|
||||
|
||||
"manual_drawer_translate", "drag_drawer_translate":
|
||||
return Tween.TransitionType.TRANS_LINEAR
|
||||
"manual_drawer_ease", "drag_drawer_ease":
|
||||
return Tween.EaseType.EASE_IN
|
||||
return null
|
||||
func _get_enum_string(className : StringName, enumName : StringName) -> String:
|
||||
var ret : String
|
||||
for constant_name in ClassDB.class_get_enum_constants(className, enumName):
|
||||
var constant_value: int = ClassDB.class_get_integer_constant(className, constant_name)
|
||||
ret += "%s:%d, " % [constant_name, constant_value]
|
||||
return ret.left(-2).replace("_", " ").capitalize().replace(", ", ",")
|
||||
func _convert_to_enum(strs : PackedStringArray) -> String:
|
||||
return ", ".join(strs).replace("_", " ").capitalize().replace(", ", ",")
|
||||
|
||||
|
||||
|
||||
func _confirm_input_accept(event : InputEvent, drag : bool = false) -> bool:
|
||||
if mouse_filter == MouseFilter.MOUSE_FILTER_IGNORE: return false
|
||||
|
||||
var boundType : InputAreaMode
|
||||
if _state:
|
||||
boundType = close_bounds
|
||||
if drag && !(allow_drag & DragMode.ON_CLOSE):
|
||||
return false
|
||||
else:
|
||||
boundType = open_bounds
|
||||
if drag && !(allow_drag & DragMode.ON_OPEN):
|
||||
return false
|
||||
|
||||
match boundType:
|
||||
InputAreaMode.Nowhere:
|
||||
return false
|
||||
InputAreaMode.Anywhere:
|
||||
pass
|
||||
InputAreaMode.WithinBounds:
|
||||
if !get_rect().has_point(event.position):
|
||||
return false
|
||||
InputAreaMode.ExcludeDrawer:
|
||||
if get_drawer_rect().has_point(event.position):
|
||||
if mouse_filter == MouseFilter.MOUSE_FILTER_STOP:
|
||||
get_viewport().set_input_as_handled()
|
||||
return false
|
||||
InputAreaMode.WithinEmptyBounds:
|
||||
if get_rect().intersection(get_drawer_rect()).has_point(event.position):
|
||||
if mouse_filter == MouseFilter.MOUSE_FILTER_STOP:
|
||||
get_viewport().set_input_as_handled()
|
||||
return false
|
||||
|
||||
if mouse_filter == MouseFilter.MOUSE_FILTER_STOP:
|
||||
get_viewport().set_input_as_handled()
|
||||
return true
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton || event is InputEventScreenTouch:
|
||||
if event.pressed:
|
||||
if !_is_dragging:
|
||||
if action_mode & ActionMode.ACTION_MODE_BUTTON_PRESS:
|
||||
if !_confirm_input_accept(event, false): return
|
||||
_toggle_drawer(!is_open())
|
||||
return
|
||||
|
||||
if action_mode & ActionMode.ACTION_MODE_BUTTON_DRAG:
|
||||
if !_confirm_input_accept(event, true): return
|
||||
drag_start.emit()
|
||||
_is_dragging = true
|
||||
else:
|
||||
if action_mode & ActionMode.ACTION_MODE_BUTTON_RELEASE:
|
||||
if _confirm_input_accept(event, false) && !_has_dragged:
|
||||
_toggle_drawer(!is_open())
|
||||
|
||||
_has_dragged = false
|
||||
_is_dragging = false
|
||||
_drag_value = 0.0
|
||||
return
|
||||
|
||||
if action_mode & ActionMode.ACTION_MODE_BUTTON_DRAG:
|
||||
if _is_dragging:
|
||||
drag_end.emit()
|
||||
if is_open():
|
||||
_toggle_drawer(_drag_value > -open_drag_threshold, true)
|
||||
else:
|
||||
_toggle_drawer(_drag_value > close_drag_threshold, true)
|
||||
|
||||
set_deferred("_has_dragged", false)
|
||||
set_deferred("_is_dragging", false)
|
||||
set_deferred("_drag_value", 0.0)
|
||||
return
|
||||
|
||||
if _is_dragging:
|
||||
if event is InputEventMouseMotion || event is InputEventScreenDrag:
|
||||
var projected_scalar : float = event.relative.dot(_angle_vec) / _angle_vec.length_squared()
|
||||
_drag_value += projected_scalar
|
||||
|
||||
if is_zero_approx(_drag_value):
|
||||
_has_dragged = true
|
||||
|
||||
_progress_changed(get_progress_adjusted(true))
|
||||
if smooth_drag: _adjust_children()
|
||||
|
||||
|
||||
|
||||
# Overload Functions
|
||||
## Used by [method get_drawer_offset] to calculate the offset of the drawer, given the current progress.
|
||||
## Overload this method to create custom opening/closing behavior.
|
||||
func _get_drawer_offset(inner_offset : Vector2, outer_offset : Vector2, with_drag : bool = false) -> Vector2:
|
||||
#return (outer_offset - inner_offset) * get_progress_adjusted(with_drag) + inner_offset
|
||||
return inner_offset.lerp(outer_offset, get_progress_adjusted(with_drag))
|
||||
|
||||
# Virtual Functions
|
||||
|
||||
## A virtual function that is is called whenever the drawer progress changes.
|
||||
func _progress_changed(progress : float) -> void: pass
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,222 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name PaddingContainer extends Container
|
||||
## A [Container] that provides percentage and numerical padding to it's children.
|
||||
|
||||
|
||||
## If [code]true[/code], this [Container]'s minimum size will update according to it's
|
||||
## children and numerical pixel padding.
|
||||
@export var minimum_size : bool = true:
|
||||
set(val):
|
||||
if minimum_size != val:
|
||||
minimum_size = val
|
||||
|
||||
update_minimum_size()
|
||||
|
||||
## The percentage left padding.
|
||||
var child_anchor_left : float = 0:
|
||||
set(val):
|
||||
if child_anchor_left != val:
|
||||
child_anchor_left = val
|
||||
|
||||
child_anchor_right = max(val, child_anchor_right)
|
||||
## The percentage top padding.
|
||||
var child_anchor_top : float = 0:
|
||||
set(val):
|
||||
if child_anchor_top != val:
|
||||
child_anchor_top = val
|
||||
|
||||
child_anchor_bottom = max(val, child_anchor_bottom)
|
||||
## The percentage right padding.
|
||||
var child_anchor_right : float = 1:
|
||||
set(val):
|
||||
if child_anchor_right != val:
|
||||
child_anchor_right = val
|
||||
|
||||
child_anchor_left = min(val, child_anchor_left)
|
||||
## The percentage bottom padding.
|
||||
var child_anchor_bottom : float = 1:
|
||||
set(val):
|
||||
if child_anchor_bottom != val:
|
||||
child_anchor_bottom = val
|
||||
|
||||
child_anchor_top = min(val, child_anchor_top)
|
||||
|
||||
## The numerical pixel left padding.
|
||||
var child_offset_left : int = 0:
|
||||
set(val):
|
||||
if child_offset_left != val:
|
||||
child_offset_left = val
|
||||
|
||||
update_minimum_size()
|
||||
## The numerical pixel top padding.
|
||||
var child_offset_top : int = 0:
|
||||
set(val):
|
||||
if child_offset_top != val:
|
||||
child_offset_top = val
|
||||
|
||||
update_minimum_size()
|
||||
## The numerical pixel right padding.
|
||||
var child_offset_right : int = 0:
|
||||
set(val):
|
||||
if child_offset_right != val:
|
||||
child_offset_right = val
|
||||
|
||||
update_minimum_size()
|
||||
## The numerical pixel bottom padding.
|
||||
var child_offset_bottom : int = 0:
|
||||
set(val):
|
||||
if child_offset_bottom != val:
|
||||
child_offset_bottom = val
|
||||
|
||||
update_minimum_size()
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
sort_children.connect(_handel_resize)
|
||||
func _ready() -> void:
|
||||
_handel_resize()
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
var properties : Array[Dictionary] = []
|
||||
|
||||
properties.append({
|
||||
"name" = "Anchors",
|
||||
"type" = TYPE_NIL,
|
||||
"usage" = PROPERTY_USAGE_GROUP,
|
||||
"hint_string" = "child_anchor_"
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_anchor_left",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_greater,or_less"
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_anchor_top",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_greater,or_less"
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_anchor_right",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_greater,or_less"
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_anchor_bottom",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,1,0.001,or_greater,or_less"
|
||||
})
|
||||
|
||||
properties.append({
|
||||
"name" = "Offsets",
|
||||
"type" = TYPE_NIL,
|
||||
"usage" = PROPERTY_USAGE_GROUP,
|
||||
"hint_string" = "child_offset_"
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_offset_left",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_offset_top",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_offset_right",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
properties.append({
|
||||
"name": "child_offset_bottom",
|
||||
"type": TYPE_FLOAT,
|
||||
"usage": PROPERTY_USAGE_DEFAULT
|
||||
})
|
||||
|
||||
return properties
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
if property in [
|
||||
"child_anchor_left",
|
||||
"child_anchor_top",
|
||||
]:
|
||||
return self[property] != 0.0
|
||||
elif property in [
|
||||
"child_anchor_right",
|
||||
"child_anchor_bottom",
|
||||
]:
|
||||
return self[property] != 1.0
|
||||
elif property in [
|
||||
"child_offset_left",
|
||||
"child_offset_top",
|
||||
"child_offset_right",
|
||||
"child_offset_bottom"
|
||||
]:
|
||||
return self[property] != 0
|
||||
return false
|
||||
func _property_get_revert(property: StringName) -> Variant:
|
||||
if property in [
|
||||
"child_anchor_left",
|
||||
"child_anchor_top",
|
||||
]:
|
||||
return 0.0
|
||||
elif property in [
|
||||
"child_anchor_right",
|
||||
"child_anchor_bottom",
|
||||
]:
|
||||
return 1.0
|
||||
elif property in [
|
||||
"child_offset_left",
|
||||
"child_offset_top",
|
||||
"child_offset_right",
|
||||
"child_offset_bottom"
|
||||
]:
|
||||
return 0
|
||||
return null
|
||||
|
||||
func _get_minimum_size() -> Vector2:
|
||||
if !minimum_size: return Vector2.ZERO
|
||||
|
||||
var min : Vector2
|
||||
for child : Node in get_children():
|
||||
if child is Control:
|
||||
min = min.max(child.get_combined_minimum_size())
|
||||
|
||||
min += Vector2(
|
||||
child_offset_left + child_offset_right,
|
||||
child_offset_top + child_offset_bottom
|
||||
)
|
||||
|
||||
return min
|
||||
|
||||
func _handel_resize() -> void:
|
||||
for child in get_children():
|
||||
if child is Control:
|
||||
child.set_anchor_and_offset(
|
||||
SIDE_LEFT,
|
||||
child_anchor_left,
|
||||
child_offset_left
|
||||
)
|
||||
child.set_anchor_and_offset(
|
||||
SIDE_TOP,
|
||||
child_anchor_top,
|
||||
child_offset_top
|
||||
)
|
||||
child.set_anchor_and_offset(
|
||||
SIDE_RIGHT,
|
||||
child_anchor_right,
|
||||
-child_offset_right
|
||||
)
|
||||
child.set_anchor_and_offset(
|
||||
SIDE_BOTTOM,
|
||||
child_anchor_bottom,
|
||||
-child_offset_bottom
|
||||
)
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,188 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name ProportionalContainer extends Container
|
||||
## A container that preserves the proportions of its [member ancher] size.
|
||||
## [br][br]
|
||||
## [b]WARNING[b]: Is this can cause crashes if misused. Try to use [PaddingContainer] instead, unless required.
|
||||
|
||||
|
||||
## The method this node will change in proportion of its [member ancher] size.
|
||||
enum PROPORTION_MODE {
|
||||
NONE = 0b000, ## No action. Minimum size will be set at [constant Vector2.ZERO].
|
||||
WIDTH = 0b101, ## Same as [WIDTH_PROPORTION], but also sets children height to be equal to the [member ancher]'s size's height.
|
||||
WIDTH_PROPORTION = 0b001, ## Sets the minimum width to be equal to the [member ancher] width multipled by [member horizontal_ratio].
|
||||
HEIGHT = 0b110, ## Same as [HEIGHT_PROPORTION], but also sets children height to be equal to the [member ancher]'s size's width.
|
||||
HEIGHT_PROPORTION = 0b010, ## Sets the minimum height to be equal to the [member ancher] height multipled by [member vertical_ratio].
|
||||
BOTH = 0b011 ## Sets the minimum size to be equal to the [member ancher] size multipled by [member horizontal_ratio] and [member vertical_ratio] respectively.
|
||||
}
|
||||
|
||||
|
||||
|
||||
@export_group("Ancher")
|
||||
## The ancher node this container proportions itself to. Is used if [member ancher_to_parent] is [code]false[/code].
|
||||
## [br][br]
|
||||
## If [code]null[/code], then this container proportions itself to it's parent control size.
|
||||
@export var ancher : Control:
|
||||
set(val):
|
||||
if ancher != val:
|
||||
if ancher && ancher.resized.is_connected(_sort_children):
|
||||
ancher.resized.disconnect(_sort_children)
|
||||
if val && !val.resized.is_connected(_sort_children):
|
||||
val.resized.connect(_sort_children)
|
||||
ancher = val
|
||||
queue_sort()
|
||||
|
||||
@export_group("Proportion")
|
||||
## The proportion mode used to scale itself to the [member ancher].
|
||||
@export var mode : PROPORTION_MODE = PROPORTION_MODE.NONE:
|
||||
set(val):
|
||||
if mode != val:
|
||||
mode = val
|
||||
notify_property_list_changed()
|
||||
queue_sort()
|
||||
## The multiplicative of this node's width to the [member ancher] width.
|
||||
@export_range(0., 1., 0.001, "or_greater") var horizontal_ratio : float = 1.:
|
||||
set(val):
|
||||
if horizontal_ratio != val:
|
||||
horizontal_ratio = val
|
||||
queue_sort()
|
||||
## The multiplicative of this node's height to the [member ancher] height.
|
||||
@export_range(0., 1., 0.001, "or_greater") var vertical_ratio : float = 1.:
|
||||
set(val):
|
||||
if vertical_ratio != val:
|
||||
vertical_ratio = val
|
||||
queue_sort()
|
||||
|
||||
|
||||
|
||||
var _min_size : Vector2
|
||||
var _ignore_resize : bool
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
layout_mode = 0
|
||||
sort_children.connect(_sort_children)
|
||||
func _ready() -> void:
|
||||
_sort_children()
|
||||
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name in [
|
||||
"layout_mode",
|
||||
"size",
|
||||
]:
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name == "horizontal_ratio":
|
||||
if !(mode & PROPORTION_MODE.WIDTH_PROPORTION):
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
elif property.name == "vertical_ratio":
|
||||
if !(mode & PROPORTION_MODE.HEIGHT_PROPORTION):
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
func _get_minimum_size() -> Vector2:
|
||||
return _min_size
|
||||
|
||||
|
||||
|
||||
func _sort_children() -> void:
|
||||
if _ignore_resize: return
|
||||
_ignore_resize = true
|
||||
|
||||
if mode != PROPORTION_MODE.NONE:
|
||||
# Sets the min size according to it's dimentions and proportion mode
|
||||
|
||||
var ancher_size : Vector2 = get_parent_area_size()
|
||||
if ancher: ancher_size = ancher.size
|
||||
var child_min_size := _get_children_min_size()
|
||||
var _old_min_size := _min_size
|
||||
|
||||
if mode & PROPORTION_MODE.WIDTH != 0:
|
||||
_min_size.x = ancher_size.x * horizontal_ratio
|
||||
else:
|
||||
_min_size.x = child_min_size.x
|
||||
if mode & PROPORTION_MODE.HEIGHT != 0:
|
||||
_min_size.y = ancher_size.y * vertical_ratio
|
||||
else:
|
||||
_min_size.y = child_min_size.y
|
||||
|
||||
if _min_size != _old_min_size:
|
||||
update_minimum_size()
|
||||
elif _min_size != Vector2.ZERO:
|
||||
# Sets min size to default
|
||||
|
||||
_min_size = Vector2.ZERO
|
||||
update_minimum_size()
|
||||
|
||||
_ignore_resize = false
|
||||
_fit_children()
|
||||
|
||||
|
||||
|
||||
func _fit_children() -> void:
|
||||
for child : Control in _get_control_children():
|
||||
_fit_child(child)
|
||||
func _fit_child(child : Control) -> void:
|
||||
var child_size := child.get_minimum_size()
|
||||
var ancher_size : Vector2 = ancher.size if ancher else get_parent_area_size()
|
||||
var set_pos : Vector2
|
||||
|
||||
# Gets the ancher_size according to this node's dimentions and proportion mode
|
||||
if mode & PROPORTION_MODE.WIDTH_PROPORTION > 0:
|
||||
ancher_size.x = ancher_size.x * horizontal_ratio
|
||||
|
||||
# Expands or repositions child, according to ancher and size flages
|
||||
match child.size_flags_horizontal & ~SIZE_EXPAND:
|
||||
SIZE_FILL:
|
||||
child_size.x = ancher_size.x
|
||||
set_pos.x = 0
|
||||
SIZE_SHRINK_BEGIN:
|
||||
child_size.x = max(child_size.x, ancher_size.x)
|
||||
set_pos.x = 0
|
||||
SIZE_SHRINK_CENTER:
|
||||
child_size.x = max(child_size.x, ancher_size.x)
|
||||
set_pos.x = max((ancher_size.x - child_size.x) * 0.5, 0)
|
||||
SIZE_SHRINK_END:
|
||||
child_size.x = max(child_size.x, ancher_size.x)
|
||||
set_pos.x = max(ancher_size.x - child_size.x, 0)
|
||||
if mode == PROPORTION_MODE.WIDTH:
|
||||
child_size.y = size.y
|
||||
|
||||
# Gets the ancher_size according to this node's dimentions and proportion mode
|
||||
if mode & PROPORTION_MODE.HEIGHT_PROPORTION > 0:
|
||||
ancher_size.y = ancher_size.y * vertical_ratio
|
||||
|
||||
# Expands or repositions child, according to ancher and size flages
|
||||
match child.size_flags_vertical & ~SIZE_EXPAND:
|
||||
SIZE_FILL:
|
||||
child_size.y = ancher_size.y
|
||||
set_pos.y = 0
|
||||
SIZE_SHRINK_BEGIN:
|
||||
child_size.y = max(child_size.y, ancher_size.y)
|
||||
set_pos.y = 0
|
||||
SIZE_SHRINK_CENTER:
|
||||
child_size.y = max(child_size.y, ancher_size.y)
|
||||
set_pos.y = max((size.y - child_size.y) * 0.5, 0)
|
||||
SIZE_SHRINK_END:
|
||||
child_size.y = max(child_size.y, ancher_size.y)
|
||||
set_pos.y = max(size.y - child_size.y, 0)
|
||||
if mode == PROPORTION_MODE.HEIGHT:
|
||||
child_size.x = size.x
|
||||
|
||||
fit_child_in_rect(child, Rect2(set_pos, child_size))
|
||||
func _get_children_min_size() -> Vector2:
|
||||
var ret := Vector2.ZERO
|
||||
for child : Control in _get_control_children():
|
||||
ret = ret.max(child.get_combined_minimum_size())
|
||||
return ret
|
||||
func _get_control_children() -> Array[Control]:
|
||||
var ret : Array[Control]
|
||||
ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible))
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
func _get_allowed_size_flags_horizontal() -> PackedInt32Array:
|
||||
return [SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END]
|
||||
func _get_allowed_size_flags_vertical() -> PackedInt32Array:
|
||||
return [SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END]
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,77 @@
|
|||
@tool
|
||||
class_name Page extends Container
|
||||
## A standardized [Container] node for Routers to use, such as [RouterStack].
|
||||
|
||||
|
||||
## Emits when an event is requested to the attached Router parent.
|
||||
## [br][br]
|
||||
## If this Router is a decedent of another [Page], connect that [Page]'s
|
||||
## [method emit_event] with this [Signal].
|
||||
signal event_action(event_name : String, args : Variant)
|
||||
|
||||
@warning_ignore("unused_signal")
|
||||
## Emits when this page is added as a child and finished animation by a Router.
|
||||
signal entered
|
||||
@warning_ignore("unused_signal")
|
||||
## Emits when this page is added as a child.
|
||||
signal entering
|
||||
@warning_ignore("unused_signal")
|
||||
## Emits when this page is about to be removed as a child and finished animation by a Router.
|
||||
signal exited
|
||||
@warning_ignore("unused_signal")
|
||||
## Emits when this page is marked to be removed as a child.
|
||||
signal exiting
|
||||
|
||||
|
||||
|
||||
## Requests an event to the attached Router parent.
|
||||
func emit_event(event_name : String, args : Variant) -> void:
|
||||
event_action.emit(event_name, args)
|
||||
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
if !Engine.is_editor_hint():
|
||||
clip_contents = true
|
||||
func _init() -> void:
|
||||
sort_children.connect(_sort_children)
|
||||
|
||||
|
||||
|
||||
func _sort_children() -> void:
|
||||
for child : Node in get_children():
|
||||
if child is Control: _update_child(child)
|
||||
func _update_child(child : Control):
|
||||
var child_min_size := child.get_minimum_size()
|
||||
var result_size := child_min_size
|
||||
|
||||
var set_pos : Vector2
|
||||
match child.size_flags_horizontal & ~SIZE_EXPAND:
|
||||
SIZE_FILL:
|
||||
result_size.x = max(result_size.x, size.x)
|
||||
set_pos.x = (size.x - result_size.x) * 0.5
|
||||
SIZE_SHRINK_BEGIN:
|
||||
set_pos.x = 0
|
||||
SIZE_SHRINK_CENTER:
|
||||
set_pos.x = (size.x - result_size.x) * 0.5
|
||||
SIZE_SHRINK_END:
|
||||
set_pos.x = size.x - result_size.x
|
||||
match child.size_flags_vertical & ~SIZE_EXPAND:
|
||||
SIZE_FILL:
|
||||
result_size.y = max(result_size.y, size.y)
|
||||
set_pos.y = (size.y - result_size.y) * 0.5
|
||||
SIZE_SHRINK_BEGIN:
|
||||
set_pos.y = 0
|
||||
SIZE_SHRINK_CENTER:
|
||||
set_pos.y = (size.y - result_size.y) * 0.5
|
||||
SIZE_SHRINK_END:
|
||||
set_pos.y = size.y - result_size.y
|
||||
|
||||
fit_child_in_rect(child, Rect2(set_pos, result_size))
|
||||
|
||||
|
||||
|
||||
func _get_allowed_size_flags_horizontal() -> PackedInt32Array:
|
||||
return [SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END]
|
||||
func _get_allowed_size_flags_vertical() -> PackedInt32Array:
|
||||
return [SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END]
|
|
@ -0,0 +1,43 @@
|
|||
@tool
|
||||
class_name PageInfo extends Resource
|
||||
## A [Resource] for keeping stack of [Page] information for a Router, such as [RouterStack].
|
||||
|
||||
|
||||
var _page : Control
|
||||
var _auto_clean : bool
|
||||
var _enter_animate : SwapContainer.ANIMATION_TYPE
|
||||
var _exit_animate : SwapContainer.ANIMATION_TYPE
|
||||
|
||||
|
||||
## Static create function for this [Resource].
|
||||
static func create(
|
||||
page: Page,
|
||||
enter_animate : SwapContainer.ANIMATION_TYPE,
|
||||
exit_animate : SwapContainer.ANIMATION_TYPE,
|
||||
auto_clean : bool
|
||||
) -> PageInfo:
|
||||
var info = PageInfo.new()
|
||||
info._page = page
|
||||
info._enter_animate = enter_animate
|
||||
info._exit_animate = exit_animate
|
||||
info._auto_clean = auto_clean
|
||||
|
||||
return info
|
||||
|
||||
## Gets the current [Page] held by this [Resource].
|
||||
func get_page() -> Page: return _page
|
||||
## Gets the saved enter animation.
|
||||
func get_enter_animation() -> SwapContainer.ANIMATION_TYPE: return _enter_animate
|
||||
## Gets the saved exit animation.
|
||||
func get_exit_animation() -> SwapContainer.ANIMATION_TYPE: return _exit_animate
|
||||
|
||||
|
||||
func _notification(what):
|
||||
if (
|
||||
what == NOTIFICATION_PREDELETE &&
|
||||
_auto_clean &&
|
||||
_page &&
|
||||
is_instance_valid(_page)
|
||||
):
|
||||
_page.queue_free()
|
||||
_page = null
|
|
@ -0,0 +1,427 @@
|
|||
@tool
|
||||
class_name RouterStack extends PanelContainer
|
||||
## Handles a [Control] stack, between [Page] nodes, using [SwapContainer].
|
||||
|
||||
## The Animation type to transition with.
|
||||
const ANIMATION_TYPE = SwapContainer.ANIMATION_TYPE
|
||||
|
||||
## Emits when the current [Page] requests an event.
|
||||
signal event_action(event : String, args : Variant)
|
||||
|
||||
## Emits at the start of a transition.
|
||||
signal start_animation
|
||||
## Emits at the end of a transition.
|
||||
signal end_animation
|
||||
|
||||
## The filepath to the [Page] node this to load on ready. If this path is invaild,
|
||||
## or not to a [PackedScene] with a [Page] node root, nothing will be loaded on ready.
|
||||
@export_file("*.tscn") var starting_page : String:
|
||||
set(val):
|
||||
if val != starting_page:
|
||||
starting_page = val
|
||||
if Engine.is_editor_hint() && is_node_ready():
|
||||
_clear_all_pages()
|
||||
if ResourceLoader.exists(starting_page) && starting_page.get_extension() == "tscn":
|
||||
route(starting_page, ANIMATION_TYPE.NONE, ANIMATION_TYPE.NONE)
|
||||
## The max size of the stack. If the stack is too big, it will clear the oldest on
|
||||
## the stack first.
|
||||
@export_range(0, 1000, 1, "or_greater") var max_stack : int = 50:
|
||||
set(val):
|
||||
val = max(val, 1)
|
||||
if max_stack != val:
|
||||
max_stack = val
|
||||
|
||||
|
||||
@export_group("Animation")
|
||||
## Starts animation with the [Control] node outside of the visisble screen.
|
||||
@export var from_outside_screen : bool:
|
||||
set(val):
|
||||
if val != from_outside_screen:
|
||||
from_outside_screen = val
|
||||
_stack.from_outside_screen = val
|
||||
## Starts animation an offset of this amount of pixels (away from the center), start
|
||||
## at the position the [Control] originally would be placed at.
|
||||
@export var offset : float:
|
||||
set(val):
|
||||
if val != offset:
|
||||
offset = val
|
||||
_stack.offset = val
|
||||
|
||||
@export_group("Easing")
|
||||
## The [enum Tween.EaseType] that will be used as the new [Control] transitions in.
|
||||
@export var ease_enter : Tween.EaseType = Tween.EaseType.EASE_IN_OUT:
|
||||
set(val):
|
||||
if val != ease_enter:
|
||||
ease_enter = val
|
||||
_stack.ease_enter = val
|
||||
## The [enum Tween.EaseType] that will be used as the current [Control] transitions out.
|
||||
@export var ease_exit : Tween.EaseType = Tween.EaseType.EASE_IN_OUT:
|
||||
set(val):
|
||||
if val != ease_exit:
|
||||
ease_exit = val
|
||||
_stack.ease_exit = val
|
||||
|
||||
@export_group("Transition")
|
||||
## The [enum Tween.TransitionType] that will be used as the new [Control] transitions in.
|
||||
@export var transition_enter : Tween.TransitionType = Tween.TransitionType.TRANS_CUBIC:
|
||||
set(val):
|
||||
if val != transition_enter:
|
||||
transition_enter = val
|
||||
_stack.transition_enter = val
|
||||
## The [enum Tween.TransitionType] that will be used as the current [Control] transitions out.
|
||||
@export var transition_exit : Tween.TransitionType = Tween.TransitionType.TRANS_CUBIC:
|
||||
set(val):
|
||||
if val != transition_exit:
|
||||
transition_exit = val
|
||||
_stack.transition_exit = val
|
||||
|
||||
@export_group("Duration")
|
||||
## The duration of the animation used as the new [Control] transitions in.
|
||||
@export var duration_enter : float = 0.35:
|
||||
set(val):
|
||||
if val != duration_enter:
|
||||
duration_enter = val
|
||||
_stack.duration_enter = val
|
||||
## The duration of the animation used as the current [Control] transitions out.
|
||||
@export var duration_exit : float = 0.35:
|
||||
set(val):
|
||||
if val != duration_exit:
|
||||
duration_exit = val
|
||||
_stack.duration_exit = val
|
||||
|
||||
|
||||
var _page_stack : Array[PageInfo] = []
|
||||
var _params : Dictionary = {}
|
||||
var _stack : SwapContainer
|
||||
|
||||
|
||||
|
||||
## Emits the [Signal Page.entered] signal on the current [Page] displayed.
|
||||
## [br][br]
|
||||
## If this Router is a decedent of another [Page], connect that [Page]'s
|
||||
## [Signal Page.entered] with this method.
|
||||
func emit_entered() -> void:
|
||||
var curr_page : Page = null if _page_stack.is_empty() else _page_stack[0].get_page()
|
||||
if curr_page:
|
||||
curr_page.entered.emit()
|
||||
## Emits the [Signal Page.entering] signal on the current [Page] displayed.
|
||||
## [br][br]
|
||||
## If this Router is a decedent of another [Page], connect that [Page]'s
|
||||
## [Signal Page.entering] with this method.
|
||||
func emit_entering() -> void:
|
||||
var curr_page : Page = null if _page_stack.is_empty() else _page_stack[0].get_page()
|
||||
if curr_page:
|
||||
curr_page.entering.emit()
|
||||
## Emits the [Signal Page.exited] signal on the current [Page] displayed.
|
||||
## [br][br]
|
||||
## If this Router is a decedent of another [Page], connect that [Page]'s
|
||||
## [Signal Page.exited] with this method.
|
||||
func emit_exited() -> void:
|
||||
var curr_page : Page = null if _page_stack.is_empty() else _page_stack[0].get_page()
|
||||
if curr_page:
|
||||
curr_page.exited.emit()
|
||||
## Emits the [Signal Page.exiting] signal on the current [Page] displayed.
|
||||
## [br][br]
|
||||
## If this Router is a decedent of another [Page], connect that [Page]'s
|
||||
## [Signal Page.exiting] with this method.
|
||||
func emit_exiting() -> void:
|
||||
var curr_page : Page = null if _page_stack.is_empty() else _page_stack[0].get_page()
|
||||
if curr_page:
|
||||
curr_page.exiting.emit()
|
||||
|
||||
|
||||
## Routes to a [Page] node given by a file path to a [PackedScene].
|
||||
func route(
|
||||
page_path : String,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
params : Dictionary = {},
|
||||
args : Dictionary = {}
|
||||
) -> Page:
|
||||
var packed : PackedScene = await _ResourceLoader.new(get_tree().process_frame, page_path).finished
|
||||
if packed == null:
|
||||
push_error("An error occured while attempting to load file at filepath '", page_path, "'")
|
||||
return null
|
||||
|
||||
return await route_packed(
|
||||
packed,
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
params,
|
||||
args
|
||||
)
|
||||
## Routes to a [Page] node given by a [PackedScene].
|
||||
func route_packed(
|
||||
page_scene : PackedScene,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
params : Dictionary = {},
|
||||
args : Dictionary = {}
|
||||
) -> Page:
|
||||
if page_scene == null:
|
||||
push_error("page_scene cannot be 'null'")
|
||||
return null
|
||||
|
||||
return await route_node(
|
||||
page_scene.instantiate(),
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
params,
|
||||
args
|
||||
)
|
||||
## Routes to a given [Page] node.
|
||||
func route_node(
|
||||
page : Page,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
params : Dictionary = {},
|
||||
args : Dictionary = {}
|
||||
) -> Page:
|
||||
if !page:
|
||||
push_error("page cannot be 'null'")
|
||||
return null
|
||||
_params = params
|
||||
|
||||
var enter_page : PageInfo = PageInfo.create(
|
||||
page,
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
args.get("auto_clean", true)
|
||||
)
|
||||
|
||||
_stack.set_modifers(args)
|
||||
_append_to_page_queue(enter_page)
|
||||
|
||||
await _handle_swap(
|
||||
enter_page.get_page(),
|
||||
enter_animation,
|
||||
exit_animation
|
||||
)
|
||||
|
||||
return page
|
||||
|
||||
|
||||
## Routes to a [Page] node given by a file path to a [PackedScene]. Clears stack.
|
||||
func navigate(
|
||||
page_path : String,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
params : Dictionary = {},
|
||||
args : Dictionary = {}
|
||||
) -> Page:
|
||||
var packed : PackedScene = await _ResourceLoader.new(get_tree().process_frame, page_path).finished
|
||||
if packed == null:
|
||||
push_error("An error occured while attempting to load file at filepath '", page_path, "'")
|
||||
return null
|
||||
|
||||
return await navigate_packed(
|
||||
packed,
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
params,
|
||||
args
|
||||
)
|
||||
## Routes to a [Page] node given by a [PackedScene]. Clears stack.
|
||||
func navigate_packed(
|
||||
page_scene : PackedScene,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
params : Dictionary = {},
|
||||
args : Dictionary = {}
|
||||
) -> Page:
|
||||
if page_scene == null:
|
||||
push_error("page_scene cannot be 'null'")
|
||||
return null
|
||||
|
||||
return await navigate_node(
|
||||
page_scene.instantiate(),
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
params,
|
||||
args
|
||||
)
|
||||
## Routes to a given [Page] node. Clears stack.
|
||||
func navigate_node(
|
||||
page : Page,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
params : Dictionary = {},
|
||||
args : Dictionary = {}
|
||||
) -> Page:
|
||||
if !page:
|
||||
push_error("page cannot be 'null'")
|
||||
return null
|
||||
_params = params
|
||||
|
||||
var enter_info : PageInfo = PageInfo.create(
|
||||
page,
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
args.get("auto_clean", true)
|
||||
)
|
||||
|
||||
_stack.set_modifers(args)
|
||||
_append_to_page_queue(enter_info)
|
||||
|
||||
await _handle_swap(
|
||||
enter_info.get_page(),
|
||||
enter_animation,
|
||||
exit_animation
|
||||
)
|
||||
_clear_stack()
|
||||
|
||||
return page
|
||||
|
||||
|
||||
## Routes to the previous [Control] on the stack. If the stack is empty, nothing will happen.
|
||||
func back(
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
params : Dictionary = {},
|
||||
args : Dictionary = {}
|
||||
) -> void:
|
||||
if is_empty(): return
|
||||
_params = params
|
||||
_stack.set_modifers(args)
|
||||
|
||||
var exit_page : PageInfo = _page_stack.pop_back()
|
||||
var enter_page : PageInfo = _page_stack.back()
|
||||
|
||||
enter_page.get_page().event_action.connect(event_action.emit)
|
||||
|
||||
if enter_animation == ANIMATION_TYPE.DEFAULT:
|
||||
enter_animation = _reverse_animate(exit_page.get_exit_animation())
|
||||
if exit_animation == ANIMATION_TYPE.DEFAULT:
|
||||
exit_animation = _reverse_animate(exit_page.get_enter_animation())
|
||||
|
||||
await _handle_swap(
|
||||
enter_page.get_page(),
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
false
|
||||
)
|
||||
|
||||
|
||||
## Gets the parameters of the last route.
|
||||
func get_params() -> Dictionary:
|
||||
return _params
|
||||
## Gets the current stack size.
|
||||
func stack_size() -> int:
|
||||
return _page_stack.size()
|
||||
## Returns if the stack is empty.
|
||||
func is_empty() -> bool:
|
||||
return _page_stack.size() <= 1
|
||||
## Returns the current [Page] on display.
|
||||
func get_current_page() -> PageInfo:
|
||||
return null if _page_stack.is_empty() else _page_stack.back()
|
||||
|
||||
|
||||
func _handle_swap(
|
||||
enter_page : Page,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
front : bool = true
|
||||
) -> void:
|
||||
var exit_page : Page = _stack.get_current()
|
||||
|
||||
if enter_page:
|
||||
enter_page.entering.emit()
|
||||
if exit_page:
|
||||
exit_page.exiting.emit()
|
||||
|
||||
await _stack.swap_control(
|
||||
enter_page,
|
||||
enter_animation,
|
||||
exit_animation,
|
||||
front
|
||||
)
|
||||
|
||||
if enter_page:
|
||||
enter_page.entered.emit()
|
||||
if exit_page:
|
||||
exit_page.exited.emit()
|
||||
|
||||
|
||||
|
||||
func _append_to_page_queue(page_node: PageInfo) -> void:
|
||||
if !_page_stack.is_empty():
|
||||
_page_stack.back().get_page().event_action.disconnect(event_action.emit)
|
||||
if _page_stack.size() > max_stack:
|
||||
_page_stack.pop_front()
|
||||
_page_stack.append(page_node)
|
||||
|
||||
var page := page_node.get_page()
|
||||
if !page.event_action.is_connected(event_action.emit):
|
||||
page.event_action.connect(event_action.emit)
|
||||
|
||||
func _reverse_animate(animation : ANIMATION_TYPE) -> ANIMATION_TYPE:
|
||||
match animation:
|
||||
ANIMATION_TYPE.NONE:
|
||||
return ANIMATION_TYPE.NONE
|
||||
ANIMATION_TYPE.LEFT:
|
||||
return ANIMATION_TYPE.RIGHT
|
||||
ANIMATION_TYPE.RIGHT:
|
||||
return ANIMATION_TYPE.LEFT
|
||||
ANIMATION_TYPE.TOP:
|
||||
return ANIMATION_TYPE.BOTTOM
|
||||
ANIMATION_TYPE.BOTTOM:
|
||||
return ANIMATION_TYPE.TOP
|
||||
return ANIMATION_TYPE.NONE
|
||||
|
||||
|
||||
func _clear_stack() -> void:
|
||||
_page_stack = [_page_stack.back()]
|
||||
func _clear_all_pages() -> void:
|
||||
_page_stack = []
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_stack = SwapContainer.new()
|
||||
add_child(_stack)
|
||||
_stack.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
_stack.start_animation.connect(start_animation.emit)
|
||||
_stack.end_animation.connect(end_animation.emit)
|
||||
|
||||
_stack.from_outside_screen = from_outside_screen
|
||||
_stack.offset = offset
|
||||
|
||||
_stack.ease_enter = ease_enter
|
||||
_stack.ease_exit = ease_exit
|
||||
|
||||
_stack.transition_enter = transition_enter
|
||||
_stack.transition_exit = transition_exit
|
||||
|
||||
_stack.duration_enter = duration_enter
|
||||
_stack.duration_exit = duration_exit
|
||||
|
||||
|
||||
|
||||
class _ResourceLoader:
|
||||
signal finished(scene : PackedScene)
|
||||
|
||||
var _resource_name : String
|
||||
|
||||
func _init(check_signal : Signal, path : StringName) -> void:
|
||||
_resource_name = path
|
||||
|
||||
if !ResourceLoader.exists(_resource_name):
|
||||
push_error("Error - Invaild Resource Loaded")
|
||||
check_signal.connect(_delay_failsave, CONNECT_ONE_SHOT)
|
||||
return
|
||||
|
||||
check_signal.connect(_on_signal)
|
||||
ResourceLoader.load_threaded_request(
|
||||
_resource_name,
|
||||
"PackedScene"
|
||||
)
|
||||
|
||||
func _on_signal() -> void:
|
||||
match ResourceLoader.load_threaded_get_status(_resource_name):
|
||||
ResourceLoader.THREAD_LOAD_INVALID_RESOURCE, ResourceLoader.THREAD_LOAD_FAILED:
|
||||
finished.emit(null)
|
||||
ResourceLoader.THREAD_LOAD_LOADED:
|
||||
finished.emit(ResourceLoader.load_threaded_get(_resource_name))
|
||||
func _delay_failsave() -> void:
|
||||
finished.emit(null)
|
|
@ -0,0 +1,77 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name MaxRatioContainer extends MaxSizeContainer
|
||||
## A container that limits an axis of it's size, to a maximum value, relative
|
||||
## to the value of it's other axis.
|
||||
|
||||
## The behavior this node will exhibit based on an axis.
|
||||
enum MAX_RATIO_MODE {
|
||||
NONE, ## No maximum value for either axis on this container.
|
||||
WIDTH, ## Sets and expands children height to be proportionate of width.
|
||||
WIDTH_PROPORTION, ## Sets the maximum height value of this container to be proportionate of width.
|
||||
HEIGHT, ## Sets and expands children width to be proportionate of height.
|
||||
HEIGHT_PROPORTION ## Sets the maximum width value of this container to be proportionate of height.
|
||||
}
|
||||
|
||||
## The ratio mode used to expand and limit children.
|
||||
@export var mode : MAX_RATIO_MODE = MAX_RATIO_MODE.NONE:
|
||||
set(val):
|
||||
if val != mode:
|
||||
mode = val
|
||||
queue_sort()
|
||||
## The ratio value used to expand and limit children.
|
||||
@export_range(0.001, 10, 0.001, "or_greater") var ratio : float = 1.0:
|
||||
set(val):
|
||||
if val != ratio:
|
||||
ratio = val
|
||||
queue_sort()
|
||||
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name == "max_size":
|
||||
property.usage |= PROPERTY_USAGE_READ_ONLY
|
||||
func _get_minimum_size() -> Vector2:
|
||||
var parent := get_parent_area_size()
|
||||
var min_size := super()
|
||||
|
||||
var current_size := min_size.max(size)
|
||||
match mode:
|
||||
MAX_RATIO_MODE.NONE:
|
||||
current_size = Vector2(0, 0)
|
||||
MAX_RATIO_MODE.WIDTH, MAX_RATIO_MODE.WIDTH_PROPORTION:
|
||||
current_size = Vector2(0, minf(current_size.x * ratio, parent.y))
|
||||
MAX_RATIO_MODE.HEIGHT, MAX_RATIO_MODE.HEIGHT_PROPORTION:
|
||||
current_size = Vector2(minf(current_size.y * ratio, parent.x), 0)
|
||||
|
||||
min_size = min_size.max(current_size)
|
||||
return min_size
|
||||
|
||||
|
||||
|
||||
## Updates the _max_size according to the ratio mode and current dimentions
|
||||
func _update_children() -> void:
|
||||
var parent := get_parent_area_size()
|
||||
var min_size := get_combined_minimum_size()
|
||||
|
||||
# Adjusts max_size itself accouring to the ratio mode and current dimentions
|
||||
match mode:
|
||||
MAX_RATIO_MODE.NONE:
|
||||
_max_size = Vector2(-1, -1)
|
||||
MAX_RATIO_MODE.WIDTH:
|
||||
_max_size = Vector2(-1, minf(size.x * ratio, parent.y))
|
||||
MAX_RATIO_MODE.WIDTH_PROPORTION:
|
||||
_max_size = Vector2(-1, min(size.x * ratio, parent.y, min_size.y))
|
||||
MAX_RATIO_MODE.HEIGHT:
|
||||
_max_size = Vector2(minf(size.y * ratio, parent.x), -1)
|
||||
MAX_RATIO_MODE.HEIGHT_PROPORTION:
|
||||
_max_size = Vector2(min(size.y * ratio, parent.x, min_size.x), -1)
|
||||
|
||||
var new_size := size
|
||||
if _max_size.x >= 0:
|
||||
new_size.x = _max_size.x
|
||||
if _max_size.y >= 0:
|
||||
new_size.y = _max_size.y
|
||||
set_deferred("size", new_size)
|
||||
|
||||
super()
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,78 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name MaxSizeContainer extends Container
|
||||
## A container that limits it's size to a maximum value.
|
||||
|
||||
var _max_size := -Vector2.ONE
|
||||
## The maximum size this container can possess.
|
||||
## [br][br]
|
||||
## If one of the axis is [code]-1[/code], then it is boundless.
|
||||
@export var max_size : Vector2 = -Vector2.ONE:
|
||||
get: return _max_size
|
||||
set(val):
|
||||
_max_size = val
|
||||
queue_sort()
|
||||
|
||||
func _init() -> void:
|
||||
sort_children.connect(_handle_sort, CONNECT_DEFERRED)
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if property == "size":
|
||||
return true
|
||||
return false
|
||||
func _get_minimum_size() -> Vector2:
|
||||
var min_size : Vector2 = Vector2.ZERO
|
||||
for child : Control in _get_control_children():
|
||||
min_size = min_size.max(child.get_combined_minimum_size())
|
||||
return min_size
|
||||
func _get_control_children() -> Array[Control]:
|
||||
var ret : Array[Control]
|
||||
ret.assign(get_children().filter(func(child : Node): return child is Control && child.visible))
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
## A helper function that should be called whenever this node's size needs to be changed, or when it's children are changed.
|
||||
func _handle_sort() -> void:
|
||||
update_minimum_size()
|
||||
_update_children()
|
||||
|
||||
func _update_children() -> void:
|
||||
for child : Control in _get_control_children():
|
||||
_update_child(child)
|
||||
func _update_child(child : Control):
|
||||
var child_min_size := child.get_minimum_size()
|
||||
var set_pos : Vector2
|
||||
var result_size := Vector2(
|
||||
size.x if _max_size.x < 0 else minf(size.x, _max_size.x),
|
||||
size.y if _max_size.y < 0 else minf(size.y, _max_size.y)
|
||||
)
|
||||
|
||||
if child.size_flags_horizontal & SIZE_EXPAND_FILL:
|
||||
result_size.x = maxf(result_size.x, child_min_size.x)
|
||||
else:
|
||||
result_size.x = minf(result_size.x, child_min_size.x)
|
||||
if child.size_flags_vertical & SIZE_EXPAND_FILL:
|
||||
result_size.y = maxf(result_size.y, child_min_size.y)
|
||||
else:
|
||||
result_size.y = minf(result_size.y, child_min_size.y)
|
||||
|
||||
|
||||
match child.size_flags_horizontal & ~SIZE_EXPAND:
|
||||
SIZE_SHRINK_CENTER, SIZE_FILL:
|
||||
set_pos.x = (size.x - result_size.x) * 0.5
|
||||
SIZE_SHRINK_BEGIN:
|
||||
set_pos.x = 0
|
||||
SIZE_SHRINK_END:
|
||||
set_pos.x = size.x - result_size.x
|
||||
match child.size_flags_vertical & ~SIZE_EXPAND:
|
||||
SIZE_SHRINK_CENTER, SIZE_FILL:
|
||||
set_pos.y = (size.y - result_size.y) * 0.5
|
||||
SIZE_SHRINK_BEGIN:
|
||||
set_pos.y = 0
|
||||
SIZE_SHRINK_END:
|
||||
set_pos.y = size.y - result_size.y
|
||||
|
||||
child.position = set_pos
|
||||
child.size = result_size
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,297 @@
|
|||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
||||
@tool
|
||||
class_name SwapContainer extends Container
|
||||
## A [Container] node that provides transitions between different [Control] nodes.
|
||||
|
||||
## The Animation type to transition with.
|
||||
enum ANIMATION_TYPE {
|
||||
DEFAULT, ## The same as [constant LEFT].
|
||||
NONE, ## No Transition
|
||||
LEFT, ## Either moves towards or away the left
|
||||
RIGHT, ## Either moves towards or away the right
|
||||
TOP, ## Either moves towards or away the top
|
||||
BOTTOM ## Either moves towards or away the bottom
|
||||
}
|
||||
|
||||
## Emits at the start of a transition.
|
||||
signal start_animation
|
||||
## Emits at the end of a transition.
|
||||
signal end_animation
|
||||
|
||||
|
||||
var _enter_tween : Tween = null
|
||||
var _exit_tween : Tween = null
|
||||
var _current_node : Control
|
||||
|
||||
|
||||
@export_group("Animation")
|
||||
## Starts animation with the [Control] node outside of the visisble screen.
|
||||
@export var from_outside_screen : bool
|
||||
## Starts animation an offset of this amount of pixels (away from the center), start
|
||||
## at the position the [Control] originally would be placed at.
|
||||
@export var offset : float
|
||||
|
||||
@export_group("Easing")
|
||||
## The [enum Tween.EaseType] that will be used as the new [Control] transitions in.
|
||||
@export var ease_enter : Tween.EaseType = Tween.EaseType.EASE_IN_OUT
|
||||
## The [enum Tween.EaseType] that will be used as the current [Control] transitions out.
|
||||
@export var ease_exit : Tween.EaseType = Tween.EaseType.EASE_IN_OUT
|
||||
|
||||
@export_group("Transition")
|
||||
## The [enum Tween.TransitionType] that will be used as the new [Control] transitions in.
|
||||
@export var transition_enter : Tween.TransitionType = Tween.TransitionType.TRANS_CUBIC
|
||||
## The [enum Tween.TransitionType] that will be used as the current [Control] transitions out.
|
||||
@export var transition_exit : Tween.TransitionType = Tween.TransitionType.TRANS_CUBIC
|
||||
|
||||
@export_group("Duration")
|
||||
## The duration of the animation used as the new [Control] transitions in.
|
||||
@export var duration_enter : float = 0.35
|
||||
## The duration of the animation used as the current [Control] transitions out.
|
||||
@export var duration_exit : float = 0.35
|
||||
|
||||
|
||||
## Causes the current [Control] node to transition out and [param node]
|
||||
## to transition in.
|
||||
func swap_control(
|
||||
node : Control,
|
||||
enter_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
exit_animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
front : bool = true
|
||||
) -> Control:
|
||||
var _old_node := _current_node
|
||||
_parent_control(node, front)
|
||||
|
||||
start_animation.emit()
|
||||
_current_node = node
|
||||
await _perform_animations(
|
||||
node,
|
||||
_old_node,
|
||||
enter_animation,
|
||||
exit_animation
|
||||
)
|
||||
|
||||
if _old_node: _unparent_control(_old_node)
|
||||
end_animation.emit()
|
||||
|
||||
return _old_node
|
||||
## Sets all export members with a simple [Dictionary].
|
||||
func set_modifers(args : Dictionary) -> void:
|
||||
if args.has("ease_enter"):
|
||||
ease_enter = args.get("ease_enter")
|
||||
if args.has("ease_exit"):
|
||||
ease_exit = args.get("ease_exit")
|
||||
|
||||
if args.has("transition_enter"):
|
||||
transition_enter = args.get("transition_enter")
|
||||
if args.has("transition_exit"):
|
||||
transition_exit = args.get("transition_exit")
|
||||
|
||||
if args.has("duration_enter"):
|
||||
duration_enter = args.get("duration_enter")
|
||||
if args.has("duration_exit"):
|
||||
duration_exit = args.get("duration_exit")
|
||||
|
||||
|
||||
## Gets the current [Control] displayed. [code]null[/code] if there is currently no such
|
||||
## [Control].
|
||||
func get_current() -> Control:
|
||||
return _current_node
|
||||
|
||||
|
||||
|
||||
func _parent_control(node: Control, front : bool) -> void:
|
||||
if !node: return
|
||||
if !node.get_parent():
|
||||
add_child(node)
|
||||
|
||||
if is_inside_tree():
|
||||
node.hide()
|
||||
get_tree().process_frame.connect(node.show, CONNECT_ONE_SHOT)
|
||||
|
||||
if front:
|
||||
node.move_to_front()
|
||||
else:
|
||||
move_child(node, 0)
|
||||
|
||||
node.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
func _unparent_control(node: Control) -> void:
|
||||
remove_child(node)
|
||||
|
||||
|
||||
|
||||
func _perform_animations(
|
||||
enter_node: Control,
|
||||
exit_node: Control,
|
||||
enter_animation: ANIMATION_TYPE,
|
||||
exit_animation: ANIMATION_TYPE
|
||||
) -> void:
|
||||
if enter_animation == ANIMATION_TYPE.NONE && exit_animation == ANIMATION_TYPE.NONE:
|
||||
if enter_node:
|
||||
enter_node.position = Vector2.ZERO
|
||||
return
|
||||
_tween_settup()
|
||||
|
||||
if enter_node:
|
||||
_handle_enter_animation(
|
||||
enter_node,
|
||||
_enter_tween,
|
||||
enter_animation
|
||||
)
|
||||
else:
|
||||
_enter_tween.finished.emit()
|
||||
_enter_tween.kill()
|
||||
_enter_tween = null
|
||||
if exit_node:
|
||||
_handle_exit_animation(
|
||||
exit_node,
|
||||
_exit_tween,
|
||||
exit_animation,
|
||||
)
|
||||
else:
|
||||
_exit_tween.finished.emit()
|
||||
_exit_tween.kill()
|
||||
_exit_tween = null
|
||||
|
||||
await _await_animations()
|
||||
func _handle_enter_animation(
|
||||
node: Control,
|
||||
animation_tween : Tween,
|
||||
animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
) -> void:
|
||||
var border := _get_border()
|
||||
|
||||
match animation:
|
||||
ANIMATION_TYPE.DEFAULT, ANIMATION_TYPE.LEFT:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.x = val,
|
||||
border.position.x,
|
||||
0,
|
||||
duration_enter
|
||||
)
|
||||
ANIMATION_TYPE.RIGHT:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.x = val,
|
||||
border.size.x - border.position.x,
|
||||
0,
|
||||
duration_enter
|
||||
)
|
||||
ANIMATION_TYPE.TOP:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.y = val,
|
||||
border.position.y,
|
||||
0,
|
||||
duration_enter
|
||||
)
|
||||
ANIMATION_TYPE.BOTTOM:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.y = val,
|
||||
border.size.y - border.position.y,
|
||||
0,
|
||||
duration_enter
|
||||
)
|
||||
_:
|
||||
node.position = Vector2.ZERO
|
||||
return
|
||||
|
||||
animation_tween.play()
|
||||
func _handle_exit_animation(
|
||||
node: Control,
|
||||
animation_tween : Tween,
|
||||
animation: ANIMATION_TYPE = ANIMATION_TYPE.DEFAULT,
|
||||
) -> void:
|
||||
var border : Rect2 = _get_border()
|
||||
|
||||
match animation:
|
||||
ANIMATION_TYPE.DEFAULT, ANIMATION_TYPE.LEFT:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.x = val,
|
||||
0,
|
||||
border.size.x - border.position.x,
|
||||
duration_enter
|
||||
)
|
||||
ANIMATION_TYPE.RIGHT:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.x = val,
|
||||
0,
|
||||
border.position.x,
|
||||
duration_enter
|
||||
)
|
||||
ANIMATION_TYPE.TOP:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.y = val,
|
||||
0,
|
||||
border.size.y - border.position.y,
|
||||
duration_enter
|
||||
)
|
||||
ANIMATION_TYPE.BOTTOM:
|
||||
animation_tween.tween_method(
|
||||
func (val): node.position.y = val,
|
||||
0,
|
||||
border.position.y,
|
||||
duration_enter
|
||||
)
|
||||
_:
|
||||
node.position = Vector2.ZERO
|
||||
return
|
||||
|
||||
animation_tween.play()
|
||||
|
||||
|
||||
func _tween_settup() -> void:
|
||||
if _enter_tween && _enter_tween.is_running():
|
||||
_enter_tween.finished.emit()
|
||||
_enter_tween.kill()
|
||||
_enter_tween = create_tween()
|
||||
|
||||
_enter_tween.set_ease(ease_enter)
|
||||
_enter_tween.set_trans(transition_enter)
|
||||
_enter_tween.stop()
|
||||
|
||||
if _exit_tween && _exit_tween.is_running():
|
||||
_exit_tween.finished.emit()
|
||||
_exit_tween.kill()
|
||||
_exit_tween = create_tween()
|
||||
|
||||
_exit_tween.set_ease(ease_exit)
|
||||
_exit_tween.set_trans(transition_exit)
|
||||
_exit_tween.stop()
|
||||
|
||||
func _get_border() -> Rect2:
|
||||
var boarder : Rect2
|
||||
|
||||
if from_outside_screen:
|
||||
boarder.position = -global_position - size
|
||||
boarder.size = get_viewport_rect().size - size
|
||||
else:
|
||||
boarder.position = -size
|
||||
boarder.size = Vector2.ZERO
|
||||
|
||||
boarder.position -= Vector2(offset, offset)
|
||||
return boarder
|
||||
|
||||
|
||||
func _await_animations() -> void:
|
||||
@warning_ignore("incompatible_ternary")
|
||||
await _SignalMerge.new(
|
||||
_enter_tween.finished if _enter_tween && _enter_tween.is_running() else null,
|
||||
_exit_tween.finished if _exit_tween && _exit_tween.is_running() else null
|
||||
) .finished
|
||||
|
||||
|
||||
class _SignalMerge:
|
||||
signal finished
|
||||
var _activate : bool = false
|
||||
|
||||
func _init(enter, exit) -> void:
|
||||
_register(enter)
|
||||
_register(exit)
|
||||
func _register(arg) -> void:
|
||||
if arg is Signal:
|
||||
arg.connect(_unleash)
|
||||
return
|
||||
_unleash()
|
||||
func _unleash() -> void:
|
||||
if _activate: finished.emit()
|
||||
_activate = true
|
||||
|
||||
# Made by Xavier Alvarez. A part of the "FreeControl" Godot addon.
|
|
@ -0,0 +1,109 @@
|
|||
@tool
|
||||
class_name ModulateTransitionContainer extends Container
|
||||
## A [Control] node with changable that allows easy [member CanvasItem.modulate] animation between colors.
|
||||
|
||||
|
||||
|
||||
@export_group("Alpha Override")
|
||||
## The colors to animate between.
|
||||
@export var colors : PackedColorArray = [Color.WHITE, Color(1.0, 1.0, 1.0, 0.5)]:
|
||||
set(val):
|
||||
if colors != val:
|
||||
colors = val
|
||||
focused_color = focused_color
|
||||
force_color(_focused_color)
|
||||
var _focused_color : int = 0
|
||||
## The index of currently used color from [member colors].
|
||||
## This member is [code]-1[/code] if [member colors] is empty.
|
||||
@export var focused_color : int:
|
||||
get: return _focused_color
|
||||
set(val):
|
||||
if colors.size() == 0:
|
||||
_focused_color = -1
|
||||
return
|
||||
|
||||
val = clampi(val, 0, colors.size() - 1)
|
||||
if _focused_color != val:
|
||||
_focused_color = val
|
||||
_on_set_color()
|
||||
## If [code]true[/code] this node will only animate over [member CanvasItem.self_modulate]. Otherwise,
|
||||
## it will animate over [member CanvasItem.modulate].
|
||||
@export var modulate_self : bool = false
|
||||
|
||||
@export_group("Tween Override")
|
||||
## The duration of color animations.
|
||||
@export var transitionTime : float = 0.2
|
||||
## The [Tween.EaseType] of color animations.
|
||||
@export var easeType : Tween.EaseType = Tween.EaseType.EASE_OUT_IN
|
||||
## The [Tween.TransitionType] of color animations.
|
||||
@export var transition : Tween.TransitionType = Tween.TransitionType.TRANS_CIRC
|
||||
## If [code]true[/code] animations can be interupted midway. Otherwise, any change in the [param focused_color]
|
||||
## will be queued to be reflected after any currently running animation.
|
||||
@export var can_cancle : bool = true
|
||||
|
||||
|
||||
var _color_tween : Tween = null
|
||||
var _current_focused_color : int
|
||||
|
||||
|
||||
## Sets the current color index.
|
||||
## [br][br]
|
||||
## Also see: [member focused_color].
|
||||
func set_color(color: int) -> void:
|
||||
focused_color = color
|
||||
## Sets the current color index. Performing this will ignore any animation and instantly set the color.
|
||||
## [br][br]
|
||||
## Also see: [member focused_color].
|
||||
func force_color(color: int) -> void:
|
||||
if _color_tween && _color_tween.is_running():
|
||||
if !can_cancle: return
|
||||
_color_tween.kill()
|
||||
_current_focused_color = color
|
||||
modulate = colors[color]
|
||||
|
||||
## Gets the current color attributed to the current color index.
|
||||
func get_current_color() -> Color:
|
||||
if _focused_color == -1: return 1
|
||||
return colors[_focused_color]
|
||||
|
||||
|
||||
|
||||
func _on_set_color():
|
||||
if _focused_color == _current_focused_color:
|
||||
return
|
||||
if can_cancle:
|
||||
if _color_tween: _color_tween.kill()
|
||||
elif _color_tween && _color_tween.is_running():
|
||||
return
|
||||
_current_focused_color = _focused_color
|
||||
|
||||
_color_tween = create_tween()
|
||||
_color_tween.tween_property(
|
||||
self,
|
||||
"self_modulate" if modulate_self else "modulate",
|
||||
get_current_color(),
|
||||
transitionTime
|
||||
)
|
||||
_color_tween.finished.connect(_on_set_color, CONNECT_ONE_SHOT)
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_current_focused_color = _focused_color
|
||||
sort_children.connect(_handle_children)
|
||||
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
if property == "colors":
|
||||
return colors.size() == 2 && colors[0] == Color.WHITE && colors[1] == Color(1.0, 1.0, 1.0, 0.5)
|
||||
return false
|
||||
|
||||
|
||||
func _handle_children() -> void:
|
||||
for child in get_children():
|
||||
fit_child_in_rect(child, Rect2(Vector2.ZERO, size))
|
||||
func _get_minimum_size() -> Vector2:
|
||||
var min_size : Vector2
|
||||
for child : Node in get_children():
|
||||
if child is Control:
|
||||
min_size = min_size.max(child.get_combined_minimum_size())
|
||||
return min_size
|
|
@ -0,0 +1,126 @@
|
|||
@tool
|
||||
class_name StyleTransitionContainer extends Container
|
||||
## A [Container] node that add a [StyleTransitionPanel] node as the background.
|
||||
|
||||
|
||||
|
||||
@export_group("Appearence Override")
|
||||
## The stylebox used by [StyleTransitionPanel].
|
||||
@export var background : StyleBox:
|
||||
set(val):
|
||||
if _panel:
|
||||
_panel.add_theme_stylebox_override("panel", val)
|
||||
background = val
|
||||
elif background != val:
|
||||
background = val
|
||||
|
||||
@export_group("Colors Override")
|
||||
## The colors to animate between.
|
||||
@export var colors : PackedColorArray = [
|
||||
Color.WEB_GRAY,
|
||||
Color.DIM_GRAY
|
||||
]:
|
||||
set(val):
|
||||
if _panel:
|
||||
_panel.colors = val
|
||||
colors = val
|
||||
elif colors != val:
|
||||
colors = val
|
||||
|
||||
## The index of currently used color from [member colors].
|
||||
## This member is [code]-1[/code] if [member colors] is empty.
|
||||
@export var focused_color : int:
|
||||
set(val):
|
||||
if _panel:
|
||||
_panel.focused_color = val
|
||||
focused_color = val
|
||||
elif focused_color != val:
|
||||
focused_color = val
|
||||
|
||||
@export_group("Tween Override")
|
||||
## The duration of color animations.
|
||||
@export var transitionTime : float = 0.2:
|
||||
set(val):
|
||||
if _panel:
|
||||
_panel.transitionTime = val
|
||||
transitionTime = val
|
||||
elif transitionTime != val:
|
||||
transitionTime = val
|
||||
## The [Tween.EaseType] of color animations.
|
||||
@export var easeType : Tween.EaseType = Tween.EaseType.EASE_OUT_IN:
|
||||
set(val):
|
||||
if _panel:
|
||||
_panel.easeType = val
|
||||
easeType = val
|
||||
elif easeType != val:
|
||||
easeType = val
|
||||
## The [Tween.TransitionType] of color animations.
|
||||
@export var transition : Tween.TransitionType = Tween.TransitionType.TRANS_CIRC:
|
||||
set(val):
|
||||
if _panel:
|
||||
_panel.transition = val
|
||||
transition = val
|
||||
elif transition != val:
|
||||
transition = val
|
||||
## If [code]true[/code] animations can be interupted midway. Otherwise, any change in the [param focused_color]
|
||||
## will be queued to be reflected after any currently running animation.
|
||||
@export var can_cancle : bool = true:
|
||||
set(val):
|
||||
if _panel:
|
||||
_panel.can_cancle = val
|
||||
can_cancle = val
|
||||
elif can_cancle != val:
|
||||
can_cancle = val
|
||||
|
||||
|
||||
var _panel : StyleTransitionPanel
|
||||
|
||||
|
||||
## Sets the current color index.
|
||||
## [br][br]
|
||||
## Also see: [member focused_color].
|
||||
func set_color(color: int) -> void:
|
||||
if !_panel: return
|
||||
_panel.set_color(color)
|
||||
## Sets the current color index. Performing this will ignore any animation and instantly set the color.
|
||||
## [br][br]
|
||||
## Also see: [member focused_color].
|
||||
func force_color(color: int) -> void:
|
||||
if !_panel: return
|
||||
_panel.force_color(color)
|
||||
|
||||
## Gets the current color attributed to the current color index.
|
||||
func get_current_color() -> Color:
|
||||
if !_panel: return Color.BLACK
|
||||
return _panel.get_current_color()
|
||||
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_panel = StyleTransitionPanel.new()
|
||||
_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(_panel)
|
||||
move_child(_panel, 0)
|
||||
|
||||
sort_children.connect(_handle_children)
|
||||
func _ready() -> void:
|
||||
if background:
|
||||
_panel.add_theme_stylebox_override("panel", background)
|
||||
return
|
||||
background = _panel.get_theme_stylebox("panel")
|
||||
|
||||
func _handle_children() -> void:
|
||||
for child in get_children():
|
||||
fit_child_in_rect(child, Rect2(Vector2.ZERO, size))
|
||||
func _get_minimum_size() -> Vector2:
|
||||
var min_size : Vector2
|
||||
for child : Node in get_children():
|
||||
if child is Control:
|
||||
min_size = min_size.max(child.get_combined_minimum_size())
|
||||
return min_size
|
||||
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
if property == "colors":
|
||||
return colors.size() == 2 && colors[0] == Color.WEB_GRAY && colors[1] == Color.DIM_GRAY
|
||||
return false
|
|
@ -0,0 +1,106 @@
|
|||
@tool
|
||||
class_name StyleTransitionPanel extends Panel
|
||||
## A [Panel] node with changable that allows easy [member CanvasItem.self_modulate] animation between colors.
|
||||
|
||||
|
||||
|
||||
@export_group("Colors Override")
|
||||
## The colors to animate between.
|
||||
@export var colors : PackedColorArray = [
|
||||
Color.WEB_GRAY,
|
||||
Color.DIM_GRAY
|
||||
]:
|
||||
set(val):
|
||||
if colors != val:
|
||||
colors = val
|
||||
focused_color = focused_color
|
||||
force_color(_focused_color)
|
||||
var _focused_color : int = 0
|
||||
## The index of currently used color from [member colors].
|
||||
## This member is [code]-1[/code] if [member colors] is empty.
|
||||
@export var focused_color : int:
|
||||
get: return _focused_color
|
||||
set(val):
|
||||
if colors.size() == 0:
|
||||
_focused_color = -1
|
||||
return
|
||||
|
||||
val = clampi(val, 0, colors.size() - 1)
|
||||
if _focused_color != val:
|
||||
_focused_color = val
|
||||
_on_set_color()
|
||||
|
||||
@export_group("Tween Override")
|
||||
## The duration of color animations.
|
||||
@export var transitionTime : float = 0.2
|
||||
## The [Tween.EaseType] of color animations.
|
||||
@export var easeType : Tween.EaseType = Tween.EaseType.EASE_OUT_IN
|
||||
## The [Tween.TransitionType] of color animations.
|
||||
@export var transition : Tween.TransitionType = Tween.TransitionType.TRANS_CIRC
|
||||
## If [code]true[/code] animations can be interupted midway. Otherwise, any change in the [param focused_color]
|
||||
## will be queued to be reflected after any currently running animation.
|
||||
@export var can_cancle : bool = true
|
||||
|
||||
|
||||
var _color_tween : Tween = null
|
||||
var _current_focused_color : int
|
||||
|
||||
|
||||
## Sets the current color index.
|
||||
## [br][br]
|
||||
## Also see: [member focused_color].
|
||||
func set_color(color: int) -> void:
|
||||
focused_color = color
|
||||
## Sets the current color index. Performing this will ignore any animation and instantly set the color.
|
||||
## [br][br]
|
||||
## Also see: [member focused_color].
|
||||
func force_color(color: int) -> void:
|
||||
if _color_tween && _color_tween.is_running():
|
||||
if !can_cancle: return
|
||||
_color_tween.kill()
|
||||
_focused_color = color
|
||||
_safe_base_set_background()
|
||||
self_modulate = get_current_color()
|
||||
|
||||
## Gets the current color attributed to the current color index.
|
||||
func get_current_color() -> Color:
|
||||
if _focused_color == -1: return Color.BLACK
|
||||
return colors[_focused_color]
|
||||
|
||||
|
||||
|
||||
func _safe_base_set_background() -> void:
|
||||
if has_theme_stylebox_override("panel"): return
|
||||
|
||||
var background = StyleBoxFlat.new()
|
||||
background.resource_local_to_scene = true
|
||||
background.bg_color = Color.WHITE
|
||||
add_theme_stylebox_override("panel", background)
|
||||
|
||||
func _on_set_color():
|
||||
if _focused_color == _current_focused_color:
|
||||
return
|
||||
if can_cancle:
|
||||
if _color_tween: _color_tween.kill()
|
||||
elif _color_tween && _color_tween.is_running():
|
||||
return
|
||||
_current_focused_color = _focused_color
|
||||
|
||||
_safe_base_set_background()
|
||||
_color_tween = create_tween()
|
||||
_color_tween.tween_property(
|
||||
self,
|
||||
"self_modulate",
|
||||
get_current_color(),
|
||||
transitionTime
|
||||
)
|
||||
_color_tween.finished.connect(_on_set_color, CONNECT_ONE_SHOT)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_current_focused_color = _focused_color
|
||||
_safe_base_set_background()
|
||||
func _property_can_revert(property: StringName) -> bool:
|
||||
if property == "colors":
|
||||
return colors.size() == 2 && colors[0] == Color.WEB_GRAY && colors[1] == Color.DIM_GRAY
|
||||
return false
|
201
godot/addons/LICENSE
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,5 +1,9 @@
|
|||
class_name Utils
|
||||
|
||||
static func remove_children(node: Node):
|
||||
for i in range(node.get_child_count()):
|
||||
node.remove_child(node.get_child(i))
|
||||
|
||||
static func get_class_name(value: Object) -> String:
|
||||
match value.get_script():
|
||||
var script:
|
||||
|
@ -26,3 +30,20 @@ static func to_str(value: Variant) -> String:
|
|||
var props: Dictionary = inst_to_dict(value)
|
||||
return "%s %s" % [name, to_str(props)]
|
||||
_: return str(value)
|
||||
|
||||
static func propagate(node: Node, fn: StringName, args: Array, call_on_self: bool = true):
|
||||
if call_on_self and node.has_method(fn):
|
||||
node.callv(fn, args)
|
||||
|
||||
for child in node.get_children():
|
||||
propagate(child, fn, args)
|
||||
|
||||
static func propagate_input_event(node: Node, fn: StringName, event: InputEvent, call_on_self: bool = true):
|
||||
if node.get_viewport().is_input_handled() or event.is_canceled():
|
||||
return
|
||||
|
||||
if call_on_self and node.has_method(fn):
|
||||
node.callv(fn, [event])
|
||||
|
||||
for child in node.get_children():
|
||||
propagate_input_event(child, fn, event)
|
||||
|
|
|
@ -21,7 +21,7 @@ PhantomCameraManager="*res://addons/phantom_camera/scripts/managers/phantom_came
|
|||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/godot_object_serializer/plugin.cfg", "res://addons/phantom_camera/plugin.cfg")
|
||||
enabled=PackedStringArray("res://addons/FreeControl/plugin.cfg", "res://addons/godot_object_serializer/plugin.cfg", "res://addons/phantom_camera/plugin.cfg")
|
||||
|
||||
[global_group]
|
||||
|
||||
|
@ -103,12 +103,18 @@ reload={
|
|||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
inventory={
|
||||
open_inventory={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":2,"pressure":0.0,"pressed":true,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
ui_close_inventory={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":2,"pressure":0.0,"pressed":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[layer_names]
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
[gd_resource type="Resource" script_class="Inventory" load_steps=4 format=3 uid="uid://bllq6ri54q3ne"]
|
||||
[gd_resource type="Resource" script_class="Inventory" load_steps=2 format=3 uid="uid://bllq6ri54q3ne"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bqprls343ue6e" path="res://src/item.gd" id="1_uptie"]
|
||||
[ext_resource type="Script" uid="uid://dh4ytedxidq0x" path="res://src/inventory.gd" id="2_1njko"]
|
||||
[ext_resource type="Resource" uid="uid://cqfnwpmo4fyv4" path="res://resources/items/key.tres" id="2_85a8j"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("2_1njko")
|
||||
items = Array[ExtResource("1_uptie")]([ExtResource("2_85a8j"), null, null, null, null, null])
|
||||
max_capacity = null
|
||||
metadata/_custom_type_script = "uid://dh4ytedxidq0x"
|
||||
|
|
|
@ -15,8 +15,10 @@ anchor_right = 1.0
|
|||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
focus_mode = 2
|
||||
script = ExtResource("1_qw0r6")
|
||||
action_name = &"inventory"
|
||||
open_action = &"inventory"
|
||||
close_action = &"ui_close_inventory"
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
layout_mode = 1
|
||||
|
@ -27,7 +29,7 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
color = Color(0.10748, 0.10748, 0.10748, 1)
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." node_paths=PackedStringArray("item_list")]
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
clip_contents = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
|
@ -37,25 +39,16 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
script = ExtResource("2_hj2ta")
|
||||
inventory = ExtResource("3_ty45s")
|
||||
item_list = NodePath("ItemNavigation/HItemList")
|
||||
|
||||
[node name="ItemNavigation" type="HBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MoveLeft" type="Button" parent="VBoxContainer/ItemNavigation"]
|
||||
layout_mode = 2
|
||||
text = "<"
|
||||
|
||||
[node name="HItemList" type="HBoxContainer" parent="VBoxContainer/ItemNavigation"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("4_yyk2a")
|
||||
item_scene = ExtResource("5_uae8j")
|
||||
|
||||
[node name="MoveRight" type="Button" parent="VBoxContainer/ItemNavigation"]
|
||||
layout_mode = 2
|
||||
text = ">"
|
||||
|
||||
[node name="Details" type="HBoxContainer" parent="VBoxContainer" node_paths=PackedStringArray("icon_image", "description_label")]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
@ -91,7 +84,7 @@ grow_horizontal = 2
|
|||
grow_vertical = 2
|
||||
autowrap_mode = 2
|
||||
|
||||
[connection signal="pressed" from="." to="." method="set_visible"]
|
||||
[connection signal="pressed" from="VBoxContainer/ItemNavigation/MoveLeft" to="VBoxContainer/ItemNavigation/HItemList" method="move_left"]
|
||||
[connection signal="closed" from="." to="." method="hide"]
|
||||
[connection signal="opened" from="." to="." method="show"]
|
||||
[connection signal="opened" from="." to="." method="grab_focus"]
|
||||
[connection signal="selected" from="VBoxContainer/ItemNavigation/HItemList" to="VBoxContainer/Details" method="_on_updated"]
|
||||
[connection signal="pressed" from="VBoxContainer/ItemNavigation/MoveRight" to="VBoxContainer/ItemNavigation/HItemList" method="move_right"]
|
||||
|
|
116
godot/scenes/inventory2.tscn
Normal file
|
@ -0,0 +1,116 @@
|
|||
[gd_scene load_steps=10 format=3 uid="uid://cn7tgd4y67wnd"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://qvoqvonnxwfc" path="res://src/inventory_ui.gd" id="1_a0rpf"]
|
||||
[ext_resource type="Script" uid="uid://bx4wxlm5mv268" path="res://src/root_control.gd" id="1_as33y"]
|
||||
[ext_resource type="Resource" uid="uid://bllq6ri54q3ne" path="res://resources/player_inventory.tres" id="2_as33y"]
|
||||
[ext_resource type="PackedScene" uid="uid://gn8k2ir47n1m" path="res://scenes/inventory_item.tscn" id="3_tg4gd"]
|
||||
[ext_resource type="Script" uid="uid://13lxe4c4fmrp" path="res://addons/FreeControl/src/CustomClasses/Carousel/Carousel.gd" id="4_usnyx"]
|
||||
|
||||
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_as33y"]
|
||||
size = Vector2(128, 128)
|
||||
|
||||
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_tg4gd"]
|
||||
size = Vector2(128, 128)
|
||||
|
||||
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_usnyx"]
|
||||
size = Vector2(128, 64)
|
||||
|
||||
[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_sebc8"]
|
||||
size = Vector2(256, 256)
|
||||
|
||||
[node name="Inventory" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 2
|
||||
script = ExtResource("1_as33y")
|
||||
open_action = &"open_inventory"
|
||||
close_action = &"ui_close_inventory"
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.147672, 0.147672, 0.147672, 1)
|
||||
|
||||
[node name="Contents" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="InventoryUI" type="Control" parent="Contents" node_paths=PackedStringArray("carousel")]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_a0rpf")
|
||||
inventory = ExtResource("2_as33y")
|
||||
item_scene = ExtResource("3_tg4gd")
|
||||
carousel = NodePath("Carousel")
|
||||
metadata/_custom_type_script = "uid://qvoqvonnxwfc"
|
||||
|
||||
[node name="Carousel" type="Container" parent="Contents/InventoryUI"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -104.0
|
||||
offset_top = 5.0
|
||||
offset_right = -104.0
|
||||
offset_bottom = 5.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("4_usnyx")
|
||||
display_range = 4
|
||||
snap_behavior = 2
|
||||
paging_requirement = 100
|
||||
metadata/_custom_type_script = "uid://13lxe4c4fmrp"
|
||||
|
||||
[node name="ItemDetails" type="HBoxContainer" parent="Contents"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="PlayerInfo" type="VBoxContainer" parent="Contents/ItemDetails"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="Contents/ItemDetails/PlayerInfo"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
texture = SubResource("PlaceholderTexture2D_as33y")
|
||||
|
||||
[node name="TextureRect2" type="TextureRect" parent="Contents/ItemDetails/PlayerInfo"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
texture = SubResource("PlaceholderTexture2D_tg4gd")
|
||||
|
||||
[node name="TextureRect3" type="TextureRect" parent="Contents/ItemDetails/PlayerInfo"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
texture = SubResource("PlaceholderTexture2D_usnyx")
|
||||
|
||||
[node name="Display" type="VBoxContainer" parent="Contents/ItemDetails"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="Contents/ItemDetails/Display"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
texture = SubResource("PlaceholderTexture2D_sebc8")
|
||||
|
||||
[node name="Description" type="VBoxContainer" parent="Contents/ItemDetails"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label" type="Label" parent="Contents/ItemDetails/Description"]
|
||||
layout_mode = 2
|
||||
text = "a description"
|
|
@ -84,6 +84,7 @@ shape = SubResource("BoxShape3D_mx8sn")
|
|||
script = ExtResource("3_w8frs")
|
||||
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
follow_viewport_enabled = true
|
||||
|
||||
[node name="Control" type="Control" parent="CanvasLayer"]
|
||||
layout_mode = 3
|
||||
|
@ -110,6 +111,7 @@ text = "Load
|
|||
"
|
||||
|
||||
[node name="Control2" parent="CanvasLayer" instance=ExtResource("8_b121j")]
|
||||
visible = true
|
||||
|
||||
[connection signal="interacted" from="Door/Interactable" to="Door" method="_on_interact"]
|
||||
[connection signal="pressed" from="CanvasLayer/Control/HBoxContainer/SaveButton" to="CanvasLayer/Control/Persistence" method="save" binds= ["save1.sav"]]
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
class_name InputListener extends Node
|
||||
class_name InputListener extends Control
|
||||
|
||||
signal pressed(toggle_state: bool)
|
||||
signal released(toggle_state: bool)
|
||||
enum ActionEvent {
|
||||
Pressed,
|
||||
Released
|
||||
}
|
||||
|
||||
@export var toggle_state: bool
|
||||
@export var action_name: StringName
|
||||
signal opened
|
||||
signal closed
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed(action_name):
|
||||
self.toggle_state = !toggle_state
|
||||
pressed.emit(toggle_state)
|
||||
@export var input_event: ActionEvent
|
||||
@export var open_action: StringName
|
||||
@export var close_action: StringName
|
||||
|
||||
if event.is_action_released(action_name):
|
||||
released.emit(toggle_state)
|
||||
func _is_active(event: InputEvent, action: StringName) -> bool:
|
||||
match input_event:
|
||||
ActionEvent.Pressed: return event.is_action_pressed(action)
|
||||
ActionEvent.Released: return event.is_action_released(action)
|
||||
_: return false
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if _is_active(event, open_action):
|
||||
accept_event()
|
||||
opened.emit()
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if _is_active(event, close_action):
|
||||
accept_event()
|
||||
closed.emit()
|
||||
|
|
|
@ -1,17 +1,63 @@
|
|||
class_name Inventory extends Resource
|
||||
|
||||
@export var items: Array[Item]
|
||||
var initial_items: Array[ItemInstance]
|
||||
var items: Dictionary[RID, ItemInstance]
|
||||
@export var max_capacity: int = 6
|
||||
|
||||
var size: int:
|
||||
get: return items.size()
|
||||
|
||||
signal item_added(item: Item, quantity: int)
|
||||
signal item_removed(item: Item, remaining: int)
|
||||
signal updated
|
||||
|
||||
func _init():
|
||||
call_deferred("_ready")
|
||||
|
||||
func _ready():
|
||||
print(initial_items.size())
|
||||
for item in initial_items:
|
||||
items[item.get_rid()] = item
|
||||
print('setting', item)
|
||||
|
||||
func _item_eq(a: Item, b: ItemInstance) -> bool:
|
||||
return a.item == b
|
||||
|
||||
func has_item(item: Item) -> bool:
|
||||
return items.has(item.get_rid())
|
||||
|
||||
func find(item: Item) -> Option:
|
||||
match items.get(item.get_rid()):
|
||||
null: return Option.none
|
||||
var found: return Option.some(found)
|
||||
|
||||
func add_item(item: Item, quantity: int = 1):
|
||||
var rid = item.get_rid()
|
||||
var inst = items.get_or_add(rid)
|
||||
inst.quantity += quantity
|
||||
item_added.emit(item, inst.quantity)
|
||||
updated.emit()
|
||||
|
||||
func remove_item(item: Item, quantity: int = 1):
|
||||
if find(item):
|
||||
item.quantity -= quantity
|
||||
if item.quantity <= 0:
|
||||
items.erase(item.get_rid())
|
||||
item_removed.emit(item, item.quantity)
|
||||
updated.emit()
|
||||
|
||||
func _iter_continue(iter: Array) -> bool:
|
||||
return iter[0] < len(items)
|
||||
return iter[0].size()
|
||||
|
||||
func _iter_init(iter: Array) -> bool:
|
||||
iter[0] = 0
|
||||
iter[0] = items.keys()
|
||||
return _iter_continue(iter)
|
||||
|
||||
func _iter_next(iter: Array) -> bool:
|
||||
iter[0] += 1
|
||||
return _iter_continue(iter)
|
||||
|
||||
func _iter_get(iter: Variant) -> Item:
|
||||
return items[iter]
|
||||
func _iter_get(iter: Variant) -> ItemInstance:
|
||||
var rid = iter[0]
|
||||
iter.remove_at(0)
|
||||
return items.get(rid)
|
||||
|
|
|
@ -1,13 +1,41 @@
|
|||
class_name InventoryUI extends VBoxContainer
|
||||
class_name InventoryUI extends Control
|
||||
|
||||
@export var inventory: Inventory
|
||||
@export var item_list: HItemList
|
||||
@export var item_scene: PackedScene
|
||||
@export var carousel: Carousel
|
||||
|
||||
func _ready() -> void:
|
||||
item_list.add_items(inventory.items)
|
||||
_build_carousel()
|
||||
inventory.updated.connect(_build_carousel)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
func _build_carousel():
|
||||
Utils.remove_children(carousel)
|
||||
|
||||
for instance in inventory:
|
||||
print('pussy', instance)
|
||||
var scene = create_item()
|
||||
bind_item(scene, Option.from(instance.item))
|
||||
carousel.add_child(scene)
|
||||
|
||||
func create_item() -> Node:
|
||||
return item_scene.instantiate()
|
||||
|
||||
func bind_item(node: Node, item: Option):
|
||||
if node.has_method('bind'):
|
||||
node.bind(item)
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed(PlayerInput.UIAction.Right):
|
||||
item_list.move_right()
|
||||
move_right()
|
||||
elif event.is_action_pressed(PlayerInput.UIAction.Left):
|
||||
item_list.move_left()
|
||||
move_left()
|
||||
|
||||
func move_by(delta: int):
|
||||
var next_index = carousel.get_carousel_index() + delta
|
||||
carousel.go_to_index(next_index)
|
||||
|
||||
func move_right():
|
||||
move_by(1)
|
||||
|
||||
func move_left():
|
||||
move_by(-1)
|
||||
|
|
24
godot/src/item_instance.gd
Normal file
|
@ -0,0 +1,24 @@
|
|||
class_name ItemInstance extends Resource
|
||||
|
||||
@export var item: Item
|
||||
@export var quantity: int
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func _init(item: Item = null, quantity: int = 1) -> void:
|
||||
self.item = item
|
||||
self.quantity = quantity
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func add_quantity(quantity: int = 1) -> int:
|
||||
self.quantity += quantity
|
||||
return self.quantity
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func remove_quantity(quantity: int = 1) -> int:
|
||||
self.quantity -= quantity
|
||||
return self.quantity
|
||||
|
||||
@warning_ignore("shadowed_variable")
|
||||
func set_quantity(quantity: int) -> int:
|
||||
self.quantity = quantity
|
||||
return self.quantity
|
|
@ -6,7 +6,7 @@ class_name ItemUI extends Control
|
|||
var _default_text: String
|
||||
var _default_icon: Texture2D
|
||||
|
||||
func _ready() -> void:
|
||||
func _init() -> void:
|
||||
_default_text = name_label.text
|
||||
_default_icon = icon_texture.texture
|
||||
|
||||
|
@ -19,5 +19,6 @@ func bind(_item: Option):
|
|||
icon_texture.texture = item.icon
|
||||
|
||||
func unbind():
|
||||
print(_default_text)
|
||||
name_label.text = _default_text
|
||||
icon_texture.texture = _default_icon
|
||||
|
|
|
@ -107,7 +107,7 @@ func _get_device(event: InputEvent) -> Device:
|
|||
else:
|
||||
return Device.Unknown
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
last_known_device = _get_device(event)
|
||||
|
||||
if event.is_action_pressed(GameAction.Interact):
|
||||
|
|
23
godot/src/root_control.gd
Normal file
|
@ -0,0 +1,23 @@
|
|||
class_name RootControl extends InputListener
|
||||
|
||||
func _ready() -> void:
|
||||
if self.focus_mode == Control.FOCUS_NONE:
|
||||
self.focus_mode = Control.FOCUS_ALL
|
||||
|
||||
opened.connect(open)
|
||||
closed.connect(close)
|
||||
|
||||
if visible:
|
||||
grab_focus()
|
||||
|
||||
func open():
|
||||
show()
|
||||
grab_focus()
|
||||
|
||||
func close():
|
||||
hide()
|
||||
release_focus()
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
super(event)
|
||||
Utils.propagate_input_event(self, "_gui_input", event, false)
|