Initial commit

This commit is contained in:
Patrick 2021-08-07 21:15:07 +02:00
commit efe9556579
Signed by: mewin
GPG Key ID: CEDB412C39B5BC47
91 changed files with 5387 additions and 0 deletions

BIN
fonts/Vera.ttf Normal file

Binary file not shown.

7
fonts/VeraLarge.tres Normal file
View File

@ -0,0 +1,7 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/fonts/Vera.ttf" type="DynamicFontData" id=1]
[resource]
size = 20
font_data = ExtResource( 1 )

6
fonts/VeraMedium.tres Normal file
View File

@ -0,0 +1,6 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/fonts/Vera.ttf" type="DynamicFontData" id=1]
[resource]
font_data = ExtResource( 1 )

BIN
fonts/VeraMono.ttf Normal file

Binary file not shown.

7
fonts/VeraMonoLarge.tres Normal file
View File

@ -0,0 +1,7 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/fonts/VeraMono.ttf" type="DynamicFontData" id=1]
[resource]
size = 20
font_data = ExtResource( 1 )

View File

@ -0,0 +1,6 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/fonts/VeraMono.ttf" type="DynamicFontData" id=1]
[resource]
font_data = ExtResource( 1 )

7
fonts/VeraMonoSmall.tres Normal file
View File

@ -0,0 +1,7 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/fonts/VeraMono.ttf" type="DynamicFontData" id=1]
[resource]
size = 12
font_data = ExtResource( 1 )

7
fonts/VeraSmall.tres Normal file
View File

@ -0,0 +1,7 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/fonts/Vera.ttf" type="DynamicFontData" id=1]
[resource]
size = 12
font_data = ExtResource( 1 )

124
fonts/Vera_COPYRIGHT.TXT Normal file
View File

@ -0,0 +1,124 @@
Bitstream Vera Fonts Copyright
The fonts have a generous copyright, allowing derivative works (as
long as "Bitstream" or "Vera" are not in the names), and full
redistribution (so long as they are not *sold* by themselves). They
can be be bundled, redistributed and sold with any software.
The fonts are distributed under the following copyright:
Copyright
=========
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
Vera is a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute
the Font Software, including without limitation the rights to use,
copy, merge, publish, distribute, and/or sell copies of the Font
Software, and to permit persons to whom the Font Software is furnished
to do so, subject to the following conditions:
The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.
The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Bitstream" or the word "Vera".
This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Bitstream Vera" names.
The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font
Software without prior written authorization from the Gnome Foundation
or Bitstream Inc., respectively. For further information, contact:
fonts at gnome dot org.
Copyright FAQ
=============
1. I don't understand the resale restriction... What gives?
Bitstream is giving away these fonts, but wishes to ensure its
competitors can't just drop the fonts as is into a font sale system
and sell them as is. It seems fair that if Bitstream can't make money
from the Bitstream Vera fonts, their competitors should not be able to
do so either. You can sell the fonts as part of any software package,
however.
2. I want to package these fonts separately for distribution and
sale as part of a larger software package or system. Can I do so?
Yes. A RPM or Debian package is a "larger software package" to begin
with, and you aren't selling them independently by themselves.
See 1. above.
3. Are derivative works allowed?
Yes!
4. Can I change or add to the font(s)?
Yes, but you must change the name(s) of the font(s).
5. Under what terms are derivative works allowed?
You must change the name(s) of the fonts. This is to ensure the
quality of the fonts, both to protect Bitstream and Gnome. We want to
ensure that if an application has opened a font specifically of these
names, it gets what it expects (though of course, using fontconfig,
substitutions could still could have occurred during font
opening). You must include the Bitstream copyright. Additional
copyrights can be added, as per copyright law. Happy Font Hacking!
6. If I have improvements for Bitstream Vera, is it possible they might get
adopted in future versions?
Yes. The contract between the Gnome Foundation and Bitstream has
provisions for working with Bitstream to ensure quality additions to
the Bitstream Vera font family. Please contact us if you have such
additions. Note, that in general, we will want such additions for the
entire family, not just a single font, and that you'll have to keep
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
glyphs to the font, they must be stylistically in keeping with Vera's
design. Vera cannot become a "ransom note" font. Jim Lyles will be
providing a document describing the design elements used in Vera, as a
guide and aid for people interested in contributing to Vera.
7. I want to sell a software package that uses these fonts: Can I do so?
Sure. Bundle the fonts with your software and sell your software
with the fonts. That is the intent of the copyright.
8. If applications have built the names "Bitstream Vera" into them,
can I override this somehow to use fonts of my choosing?
This depends on exact details of the software. Most open source
systems and software (e.g., Gnome, KDE, etc.) are now converting to
use fontconfig (see www.fontconfig.org) to handle font configuration,
selection and substitution; it has provisions for overriding font
names and subsituting alternatives. An example is provided by the
supplied local.conf file, which chooses the family Bitstream Vera for
"sans", "serif" and "monospace". Other software (e.g., the XFree86
core server) has other mechanisms for font substitution.

75
images/action.svg Normal file
View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="action.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.4"
inkscape:cx="10.477362"
inkscape:cy="2.1937508"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid817" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.03123 h 1.5875 l -0.79375,2.11667 z"
id="path4526"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<ellipse
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4530"
cy="296.20624"
cx="2.1166666"
rx="0.26458323"
ry="0.26458779" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

34
images/action.svg.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/action.svg-b508461e00794f206592b7f3abb0bf50.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/action.svg"
dest_files=[ "res://.import/action.svg-b508461e00794f206592b7f3abb0bf50.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

68
images/close.svg Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="close.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.4"
inkscape:cx="-0.93119331"
inkscape:cy="10.469104"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:none;stroke:#ffffff;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.46597373,293.23262 3.30138587,3.30139"
id="path1916"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path1918"
d="m 3.7673596,293.23262 -3.30138587,3.30139"
style="fill:none;stroke:#ffffff;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

34
images/close.svg.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/close.svg-4a9c4498aff69d70db99b31f6d40ad26.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/close.svg"
dest_files=[ "res://.import/close.svg-4a9c4498aff69d70db99b31f6d40ad26.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

63
images/favourite.svg Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="favourite.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="44.8"
inkscape:cx="5.510791"
inkscape:cy="9.1171212"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#ffb700;fill-opacity:1;stroke:none;stroke-width:0.40976673px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0.98483543,296.65544 1.15770177,-0.58209 1.1577019,0.6209 -0.200496,-1.30646 0.9184004,-0.92486 -1.3064568,-0.22638 -0.5885522,-1.16416 -0.5626821,1.19003 -1.34526253,0.18756 0.94427083,0.91194 z"
id="path6774"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/favourite.svg-e7f3ad202c86f3b36183a480a4c15217.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/favourite.svg"
dest_files=[ "res://.import/favourite.svg-e7f3ad202c86f3b36183a480a4c15217.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

63
images/favourite32.svg Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 8.4666664 8.466667"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="favourite32.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.4"
inkscape:cx="1.2576623"
inkscape:cy="7.0007595"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-288.53332)">
<path
style="fill:#ffb700;fill-opacity:1;stroke:none;stroke-width:0.81953347px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 1.9696708,296.31089 2.3154035,-1.16418 2.3154038,1.2418 -0.400992,-2.61292 1.8368008,-1.84972 -2.6129136,-0.45276 -1.1771044,-2.32832 -1.1253642,2.38006 -2.69052493,0.37512 1.88854163,1.82388 z"
id="path6774"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/favourite32.svg-c47e3f97bb8ca7f215e11bab06aac90a.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/favourite32.svg"
dest_files=[ "res://.import/favourite32.svg-c47e3f97bb8ca7f215e11bab06aac90a.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="favourite_inactive.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="44.8"
inkscape:cx="5.510791"
inkscape:cy="9.1171212"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#808080;fill-opacity:1;stroke:none;stroke-width:0.40976673px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0.98483543,296.65544 1.15770177,-0.58209 1.1577019,0.6209 -0.200496,-1.30646 0.9184004,-0.92486 -1.3064568,-0.22638 -0.5885522,-1.16416 -0.5626821,1.19003 -1.34526253,0.18756 0.94427083,0.91194 z"
id="path6774"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/favourite_inactive.svg-0714561913ff7c32510529fe22e8eec7.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/favourite_inactive.svg"
dest_files=[ "res://.import/favourite_inactive.svg-0714561913ff7c32510529fe22e8eec7.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

65
images/file.svg Normal file
View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg1968"
sodipodi:docname="file.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<defs
id="defs1962" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="5.1268212"
inkscape:cy="11.655659"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata1965">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.00157475;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 3.1875 1.4160156 L 3.1875 14.169922 L 12.753906 14.169922 L 12.753906 6.0429688 L 8.8046875 6.0429688 L 8.1582031 6.0429688 L 8.1582031 1.4160156 L 3.1875 1.4160156 z M 8.8046875 1.4160156 L 8.8046875 5.3945312 L 12.753906 5.3945312 L 8.8046875 1.4160156 z "
transform="matrix(0.26458333,0,0,0.26458333,0,292.76665)"
id="rect2533" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

34
images/file.svg.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/file.svg-4e6b8b924038da5cf70f0f5467754065.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/file.svg"
dest_files=[ "res://.import/file.svg-4e6b8b924038da5cf70f0f5467754065.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

66
images/folder.svg Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg882"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="folder.svg">
<defs
id="defs876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="44.8"
inkscape:cx="3.6511197"
inkscape:cy="9.7572085"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#c9c9c9;fill-opacity:1;stroke:none;stroke-width:0.30309412px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.9399672,293.94715 v 2.19117 H 0.29336612 v -2.51 H 1.6261685 l 0.3231055,0.31883 z"
id="path1479"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

34
images/folder.svg.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/folder.svg-7e8d363eac08897a3ba3577c7fe476ba.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/folder.svg"
dest_files=[ "res://.import/folder.svg-7e8d363eac08897a3ba3577c7fe476ba.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

66
images/folder32.svg Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 8.4666664 8.466667"
version="1.1"
id="svg882"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="folder32.svg">
<defs
id="defs876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="1.6150387"
inkscape:cy="3.5488619"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-288.53332)">
<path
style="fill:#c9c9c9;fill-opacity:1;stroke:none;stroke-width:0.60618824px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 7.8799344,290.89431 v 4.38234 H 0.58673228 v -5.02 H 3.252337 l 0.646211,0.63766 z"
id="path1479"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/folder32.svg-c88050110add556743258a22e5552a5f.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/folder32.svg"
dest_files=[ "res://.import/folder32.svg-c88050110add556743258a22e5552a5f.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

66
images/home32.svg Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 8.4666665 8.466667"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="home32.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="-20.16699"
inkscape:cy="-11.362214"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-288.53332)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.52916664px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4.9524559,294.56066 v 1.89222 H 6.741491 v -3.74176 h 0.695736 l -3.2038933,-3.6307 -3.2038939,3.6307 H 1.725176 v 3.74176 h 1.7890355 v -1.89222 z"
id="path822"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

34
images/home32.svg.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/home32.svg-6a061fc9d14c6834c99c31a441f002f7.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/home32.svg"
dest_files=[ "res://.import/home32.svg-6a061fc9d14c6834c99c31a441f002f7.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

63
images/maximize.svg Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="maximize.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="31.678384"
inkscape:cx="3.2079517"
inkscape:cy="6.5159633"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.00157475;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 1.7910156 2.6953125 C 1.4587386 2.6953125 1.1914062 2.964598 1.1914062 3.296875 L 1.1914062 13.390625 C 1.1914062 13.722902 1.4587386 13.990234 1.7910156 13.990234 L 14.208984 13.990234 C 14.541261 13.990234 14.808594 13.722902 14.808594 13.390625 L 14.808594 3.296875 C 14.808594 2.964598 14.541261 2.6953125 14.208984 2.6953125 L 1.7910156 2.6953125 z M 2.484375 3.7675781 L 13.515625 3.7675781 L 13.515625 12.919922 L 2.484375 12.919922 L 2.484375 3.7675781 z "
transform="matrix(0.26458333,0,0,0.26458333,0,292.76665)"
id="rect814" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/maximize.svg-3fa9bb18e25e21cc6b72c8baa93b174b.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/maximize.svg"
dest_files=[ "res://.import/maximize.svg-3fa9bb18e25e21cc6b72c8baa93b174b.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

67
images/minimize.svg Normal file
View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="minimize.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.4"
inkscape:cx="1.9193787"
inkscape:cy="6.0832447"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1858"
inkscape:window-height="1057"
inkscape:window-x="54"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26499999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect814"
width="3.6025856"
height="0.72051716"
x="0.3153739"
y="295.74792"
rx="0.15869126"
ry="0.15869126" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/minimize.svg-79dc3404e073f085703d8b02fd39b093.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/de.mewin.gduibasics/images/minimize.svg"
dest_files=[ "res://.import/minimize.svg-79dc3404e073f085703d8b02fd39b093.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

7
plugin.cfg Normal file
View File

@ -0,0 +1,7 @@
[plugin]
name="GDScript UI Basics"
description="Basic and advanced UI elements for Godot. (Requires GDBasics)"
author="Patrick Wuttke<mewin@mewin.de>"
version="0.1"
script="plugin.gd"

8
plugin.gd Normal file
View File

@ -0,0 +1,8 @@
tool
extends EditorPlugin
func _enter_tree():
pass
func _exit_tree():
pass

22
scenes/dynamic_tabs.tscn Normal file
View File

@ -0,0 +1,22 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/dynamic_tabs.gd" type="Script" id=1]
[node name="dynamic_tabs" type="VBoxContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="tabs" type="Tabs" parent="."]
margin_right = 1024.0
margin_bottom = 24.0
[node name="container" type="TabContainer" parent="."]
margin_top = 28.0
margin_right = 1024.0
margin_bottom = 600.0
size_flags_vertical = 3
tabs_visible = false

View File

@ -0,0 +1,32 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/editor_file_edit.gd" type="Script" id=1]
[node name="editor_file_edit" type="HBoxContainer"]
anchor_right = 1.0
margin_bottom = 24.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="line_edit" type="LineEdit" parent="."]
margin_right = 969.0
margin_bottom = 24.0
size_flags_horizontal = 3
size_flags_vertical = 0
editable = false
[node name="button" type="Button" parent="."]
margin_left = 973.0
margin_right = 1024.0
margin_bottom = 24.0
text = "Select"
[node name="file_dialog" type="FileDialog" parent="."]
margin_right = 307.0
margin_bottom = 130.0
window_title = "Open a File"
mode = 0
[connection signal="pressed" from="button" to="." method="_on_button_pressed"]
[connection signal="file_selected" from="file_dialog" to="." method="_on_file_dialog_file_selected"]

View File

@ -0,0 +1,36 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/editor_node_edit.gd" type="Script" id=1]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/node_selection_dialog.tscn" type="PackedScene" id=2]
[node name="editor_node_edit" type="HBoxContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_bottom = -576.0
script = ExtResource( 1 )
__meta__ = {
"__property_type__": 15,
"_edit_use_anchors_": false
}
[node name="line_edit" type="LineEdit" parent="."]
margin_right = 969.0
margin_bottom = 24.0
size_flags_horizontal = 3
size_flags_vertical = 0
editable = false
[node name="button" type="Button" parent="."]
margin_left = 973.0
margin_right = 1024.0
margin_bottom = 24.0
text = "Select"
[node name="node_selection_dialog" parent="." instance=ExtResource( 2 )]
margin_left = 824.0
margin_top = 0.0
margin_right = 1024.0
margin_bottom = 70.0
window_title = "Select a Node ..."
[connection signal="pressed" from="button" to="." method="_on_button_pressed"]
[connection signal="confirmed" from="node_selection_dialog" to="." method="_on_node_selection_dialog_confirmed"]

97
scenes/flexview.tscn Normal file
View File

@ -0,0 +1,97 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/dynamic_tabs.tscn" type="PackedScene" id=1]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/flexview.gd" type="Script" id=2]
[sub_resource type="StyleBoxFlat" id=1]
bg_color = Color( 0, 0.388235, 1, 0.705882 )
border_width_left = 5
border_width_top = 5
border_width_right = 5
border_width_bottom = 5
border_color = Color( 0, 0.301961, 0.796078, 0.705882 )
border_blend = true
[node name="flex_view" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="tabs" parent="." instance=ExtResource( 1 )]
drag_to_rearrange_enabled = true
drag_out_enabled = true
tab_close_display_policy = 2
tab_align = 0
[node name="drop_indicator" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 2
__meta__ = {
"_edit_use_anchors_": false
}
[node name="rect_left" type="Control" parent="drop_indicator"]
visible = false
anchor_right = 0.5
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": true
}
[node name="rect_right" type="Control" parent="drop_indicator"]
visible = false
anchor_left = 0.5
anchor_right = 1.00098
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": true
}
[node name="rect_top" type="Control" parent="drop_indicator"]
visible = false
anchor_right = 1.0
anchor_bottom = 0.5
__meta__ = {
"_edit_use_anchors_": true
}
[node name="rect_bottom" type="Control" parent="drop_indicator"]
visible = false
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": true
}
[node name="rect_center" type="Control" parent="drop_indicator"]
visible = false
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 200.0
margin_top = 117.0
margin_right = -200.0
margin_bottom = -117.0
__meta__ = {
"_edit_use_anchors_": true
}
[node name="drop_indicator" type="Panel" parent="drop_indicator"]
visible = false
modulate = Color( 1, 1, 1, 0 )
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 2
custom_styles/panel = SubResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="tween" type="Tween" parent="drop_indicator"]
[connection signal="tab_close" from="tabs" to="." method="_on_tabs_tab_close"]
[connection signal="tab_dragged_out" from="tabs" to="." method="_on_tabs_tab_dragged_out"]

View File

@ -0,0 +1,11 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/flexview_splitter.gd" type="Script" id=1]
[node name="flexview_hsplitter" type="HSplitContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}

View File

@ -0,0 +1,8 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/flexview_splitter.gd" type="Script" id=1]
[node name="flexview_hsplitter" type="VSplitContainer"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 1 )

View File

@ -0,0 +1,21 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/flexview.tscn" type="PackedScene" id=1]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/flexview_window.gd" type="Script" id=2]
[node name="flexview_window" type="WindowDialog"]
anchor_right = 1.0
anchor_bottom = 1.0
resizable = true
script = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="flex_view" parent="." instance=ExtResource( 1 )]
[connection signal="item_rect_changed" from="." to="." method="_on_flexview_window_item_rect_changed"]
[connection signal="visibility_changed" from="." to="." method="_on_flexview_window_visibility_changed"]
[connection signal="current_view_changed" from="flex_view" to="." method="_on_flex_view_current_view_changed"]
[connection signal="view_added" from="flex_view" to="." method="_on_flex_view_view_added"]
[connection signal="view_removed" from="flex_view" to="." method="_on_flex_view_view_removed"]

View File

@ -0,0 +1,27 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/line_edit_suggestion_popup.gd" type="Script" id=1]
[node name="line_edit_suggestion_popup" type="Popup"]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 0, 100 )
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="panel" type="Panel" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="v_box_container" type="VBoxContainer" parent="panel"]
anchor_right = 1.0
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": false
}

View File

@ -0,0 +1,51 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/line_input_dialog.gd" type="Script" id=1]
[node name="line_input_dialog" type="ConfirmationDialog"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_right = -524.0
margin_bottom = -499.0
rect_min_size = Vector2( 500, 90 )
window_title = "Enter a text..."
dialog_text = "Enter a text:"
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="container" type="VBoxContainer" parent="."]
margin_left = 8.0
margin_top = 22.0
margin_right = 492.0
margin_bottom = 79.0
mouse_filter = 2
__meta__ = {
"_edit_use_anchors_": false
}
[node name="control" type="Control" parent="container"]
margin_right = 484.0
margin_bottom = 5.0
rect_min_size = Vector2( 0, 5 )
[node name="line_edit" type="LineEdit" parent="container"]
margin_top = 9.0
margin_right = 484.0
margin_bottom = 33.0
size_flags_horizontal = 3
__meta__ = {
"_edit_use_anchors_": false
}
[node name="control2" type="Control" parent="container"]
margin_top = 37.0
margin_right = 484.0
margin_bottom = 57.0
rect_min_size = Vector2( 0, 20 )
mouse_filter = 2
[connection signal="about_to_show" from="." to="." method="_on_line_input_dialog_about_to_show"]
[connection signal="visibility_changed" from="." to="." method="_on_line_input_dialog_visibility_changed"]
[connection signal="text_changed" from="container/line_edit" to="." method="_on_line_edit_text_changed"]
[connection signal="text_entered" from="container/line_edit" to="." method="_on_line_edit_text_entered"]

View File

@ -0,0 +1,26 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scripts/types/controls/editor_node_tree.gd" type="Script" id=1]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/node_selection_dialog.gd" type="Script" id=2]
[node name="node_selection_dialog" type="ConfirmationDialog"]
margin_left = 233.0
margin_top = 45.0
margin_right = 843.0
margin_bottom = 516.0
script = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="tree" type="Tree" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 8.0
margin_top = 8.0
margin_right = -8.0
margin_bottom = -36.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}

View File

@ -0,0 +1,174 @@
extends Control
enum CloseButtonPolicy {
SHOW_NEVER = 0,
SHOW_ACTIVE_ONLY = 1,
SHOW_ALWAYS = 2
}
enum TabAlign {
ALIGN_LEFT = 0,
ALIGN_CENTER = 1,
ALIGN_RIGHT = 2
}
export var current_tab := 0 setget _set_current_tab, _get_current_tab
export var drag_to_rearrange_enabled := false setget _set_drag_to_rearrange_enabled
export var drag_out_enabled := false
export var show_tabs := true setget _set_show_tabs
export(CloseButtonPolicy) var tab_close_display_policy := CloseButtonPolicy.SHOW_NEVER setget _set_tab_close_display_policy
export(TabAlign) var tab_align := TabAlign.ALIGN_CENTER setget _set_tab_align
var __mouse_was_inside := false
#############
# overrides #
#############
func _ready() -> void:
$tabs.connect("tab_changed", self, "_on_tabs_tab_changed")
$tabs.connect("tab_close", self, "_on_tabs_tab_close")
$tabs.connect("reposition_active_tab_request", self, "_on_tabs_reposition_active_tab_request")
$tabs.connect("gui_input", self, "_on_tabs_gui_input")
set_process(false) # only enabled for dragging
func _process(delta : float) -> void:
if !Input.is_mouse_button_pressed(BUTTON_LEFT):
__stop_tab_dragging()
return
var mouse_pos := get_viewport().get_mouse_position()
var mouse_is_inside : bool = $tabs.get_global_rect().has_point(mouse_pos)
if mouse_is_inside == __mouse_was_inside:
return
__mouse_was_inside = mouse_is_inside
if mouse_is_inside:
emit_signal("tab_dragged_in", _get_current_tab(), mouse_pos)
else:
emit_signal("tab_dragged_out", _get_current_tab(), mouse_pos)
################
# public stuff #
################
func add_tab(title : String, control : Control, icon : Texture = null) -> int:
$tabs.add_tab(title, icon)
$container.add_child(control)
var tab := control.get_position_in_parent()
emit_signal("tab_added", tab)
return tab
func move_tab(from : int, to : int) -> void:
$tabs.move_tab(from, to)
$container.move_child($container.get_child(from), to)
func remove_tab(tab : int) -> void:
if tab == current_tab && __is_tab_dragging():
__stop_tab_dragging()
$tabs.remove_tab(tab)
$container.remove_child($container.get_child(tab))
emit_signal("tab_removed", tab)
func find_tab(control : Control) -> int:
for tab in range(get_tab_count()):
if get_tab_control(tab) == control:
return tab
return -1
func remove_all_tabs() -> void:
while $tabs.get_tab_count() > 0:
$tabs.remove_tab(0)
while $container.get_child_count() > 0:
$container.remove_child($container.get_child(0))
func get_tab_control(tab : int) -> Control:
return $container.get_tab_control(tab)
func get_tab_title(tab : int) -> String:
return $tabs.get_tab_title(tab)
func get_tabs() -> Array:
return $container.get_children()
func get_tab_count() -> int:
return $container.get_child_count()
func get_tab_rect(tab : int) -> Rect2:
var rect : Rect2 = $tabs.get_tab_rect(tab)
return $tabs.get_transform().xform_inv(rect)
#################
# private stuff #
#################
func __handle_tabs_gui_input(event : InputEvent) -> void:
if drag_out_enabled \
&& event is InputEventMouseButton \
&& event.pressed \
&& event.button_index == BUTTON_LEFT:
yield(get_tree(), "idle_frame") # wait for the original control to handle the input
var tab_rect : Rect2 = $tabs.get_tab_rect(_get_current_tab())
if tab_rect.has_point(event.position):
__start_tab_dragging()
func __start_tab_dragging() -> void:
__mouse_was_inside = true
set_process(true)
func __stop_tab_dragging() -> void:
set_process(false)
func __is_tab_dragging() -> bool:
return is_processing()
###########
# setters #
###########
func _set_current_tab(tab : int) -> void:
current_tab = tab
$tabs.current_tab = current_tab
func _get_current_tab() -> int:
return $tabs.current_tab
func _set_drag_to_rearrange_enabled(enabled : bool) -> void:
drag_to_rearrange_enabled = enabled
$tabs.drag_to_rearrange_enabled = enabled
func _set_tab_close_display_policy(policy : int) -> void:
tab_close_display_policy = policy
$tabs.tab_close_display_policy = policy
func _set_tab_align(align : int) -> void:
tab_align = align
$tabs.tab_align = align
func _set_show_tabs(show : bool) -> void:
show_tabs = show
$tabs.visible = show
###################
# signal handlers #
###################
func _on_tabs_tab_changed(tab : int) -> void:
current_tab = tab
$container.current_tab = tab
func _on_tabs_tab_close(tab : int) -> void:
emit_signal("tab_close", tab)
func _on_tabs_reposition_active_tab_request(to : int) -> void:
$container.move_child($container.get_child(_get_current_tab()), to)
current_tab = to
func _on_tabs_gui_input(event : InputEvent) -> void:
__handle_tabs_gui_input(event)
###########
# signals #
###########
signal tab_added(tab)
signal tab_removed(tab)
signal tab_close(tab)
signal tab_dragged_out(tab, position)
signal tab_dragged_in(tab, position)

View File

@ -0,0 +1,31 @@
tool
extends Control
onready var line_edit := $line_edit
onready var button := $button
onready var file_dialog := $file_dialog
export(String, FILE) var selected_file setget _set_selected_file
var file_filter = ""
func _ready():
line_edit.text = selected_file
func _set_selected_file(value : String):
if value != selected_file:
selected_file = value
if line_edit:
line_edit.text = selected_file
func _on_button_pressed():
file_dialog.clear_filters()
file_dialog.add_filter(file_filter)
file_dialog.filename = selected_file
file_dialog.popup_centered_ratio(0.5)
func _on_file_dialog_file_selected(path : String):
if path != selected_file:
_set_selected_file(path)
emit_signal("selected_file_changed")
signal selected_file_changed()

View File

@ -0,0 +1,31 @@
tool
extends Control
onready var line_edit := $line_edit
onready var button := $button
onready var node_selection_dialog := $node_selection_dialog
export(NodePath) var selected_node setget _set_selected_node
var node_type_filter = Node
func _ready():
line_edit.text = str(selected_node)
func _set_selected_node(value : NodePath):
if value != selected_node:
selected_node = value
if line_edit:
line_edit.text = str(selected_node)
func _on_button_pressed():
node_selection_dialog.get_editor_node_tree().node_type_filter = node_type_filter
node_selection_dialog.get_editor_node_tree().selected_node = selected_node
node_selection_dialog.popup_centered_ratio(0.5)
func _on_node_selection_dialog_confirmed():
var tree = node_selection_dialog.get_editor_node_tree()
if tree.selected_node != selected_node:
_set_selected_node(tree.selected_node)
emit_signal("selected_node_changed")
signal selected_node_changed()

371
scenes/scripts/flexview.gd Normal file
View File

@ -0,0 +1,371 @@
extends Control
enum SplitLocation {
TOP,
BOTTOM,
LEFT,
RIGHT
}
enum DropPosition {
TOP,
BOTTOM,
LEFT,
RIGHT,
CENTER
}
const FlexViewSplitter = preload("res://addons/de.mewin.gduibasics/scenes/scripts/flexview_splitter.gd")
const META_UNIQUE_TYPE = "__flexview_unique_type__"
const META_UNIQUE_KEY = "__flexview_unique_key__"
const META_VIEW_TITLE = "__flexview_title__"
const META_LAST_DROP_TARGET = "__flexview_last_drop_target__"
const GROUP_NAME = "__flexview__"
const INDICATOR_ANIM_DURATION = 0.1
const __HANDLERS_TO_COPY = ["view_added", "view_removed", "view_closing"]
onready var _rect_left := $drop_indicator/rect_left
onready var _rect_right := $drop_indicator/rect_right
onready var _rect_top := $drop_indicator/rect_top
onready var _rect_bottom := $drop_indicator/rect_bottom
onready var _rect_center := $drop_indicator/rect_center
onready var _drop_indicator := $drop_indicator/drop_indicator
onready var _tween := $drop_indicator/tween
var current_view := 0 setget _set_current_view, _get_current_view
export var show_tabs := true setget _set_show_tabs
var __drop_position := -1
#############
# overrides #
#############
func _ready() -> void:
set_process(false) # only used for the drop indicator
add_to_group(GROUP_NAME)
$tabs.show_tabs = show_tabs
func _process(delta : float) -> void:
var mouse_pos := get_viewport().get_mouse_position()
var drop_position : int = DropPosition.CENTER
if get_view_count() > 0: # only allow splitting if there is at least one view
var my_rect := get_global_rect()
var distances := [
(mouse_pos.y - my_rect.position.y) / my_rect.size.y,
(my_rect.position.y + my_rect.size.y - mouse_pos.y) / my_rect.size.y,
(mouse_pos.x - my_rect.position.x) / my_rect.size.x,
(my_rect.position.x + my_rect.size.x - mouse_pos.x) / my_rect.size.x
]
var idx := GDBMath.min_element(distances)
if distances[idx] < 0.25:
drop_position = idx
if __drop_position == drop_position:
return
__drop_position = drop_position
var new_rect := __get_drop_rect(drop_position)
_tween.stop(_drop_indicator, "rect_size")
_tween.stop(_drop_indicator, "rect_position")
_tween.interpolate_property(_drop_indicator, "rect_size", _drop_indicator.rect_size, new_rect.size, INDICATOR_ANIM_DURATION)
_tween.interpolate_property(_drop_indicator, "rect_position", _drop_indicator.rect_position, new_rect.position, INDICATOR_ANIM_DURATION)
_tween.start()
################
# public stuff #
################
func add_view(title : String, control : Control, make_current := true) -> int:
control.set_meta(META_VIEW_TITLE, title)
var view : int = $tabs.add_tab(get_view_title_from_control(control), control)
emit_signal("view_added", view, self)
if make_current:
_set_current_view(view)
# support for control-controlled (lel) titles
if control.has_signal("flexview_title_changed"):
control.connect("flexview_title_changed", self, "_on_flexview_title_changed")
if control.has_signal("flexview_close_me"):
control.connect("flexview_close_me", self, "_on_flexview_close_me")
return view
func remove_view(view : int) -> void:
var control := get_view_control(view)
if control:
GDBUtility.disconnect_all(control, self)
$tabs.remove_tab(view)
emit_signal("view_removed", view, self)
if get_view_count() == 0 && get_parent() is FlexViewSplitter:
self.queue_free()
func close_view(view : int) -> void:
emit_signal("view_closing", view, self)
func remove_all_views() -> void:
for view in range(get_view_count() - 1, -1, -1):
remove_view(view)
func find_view(control : Control) -> int:
return $tabs.find_tab(control)
func close_all_views() -> void:
for view in range(get_view_count() - 1, -1, -1):
close_view(view)
func set_view_title(view : int, title : String) -> void:
$tabs.set_tab_title(view, title)
func get_view_control(view : int) -> Control:
return $tabs.get_tab_control(view)
func get_view_title(view : int) -> String:
return $tabs.get_tab_title(view)
func get_views() -> Array:
return $tabs.get_tabs()
func get_view_count() -> int:
return $tabs.get_tab_count()
func split_view(placement : int, title : String, view : Control) -> Control:
var parent := get_parent()
var self_first : bool = (placement == SplitLocation.BOTTOM || placement == SplitLocation.RIGHT)
# add splitter
var splitter : SplitContainer
if placement == SplitLocation.TOP || placement == SplitLocation.BOTTOM:
splitter = preload("res://addons/de.mewin.gduibasics/scenes/flexview_vspliter.tscn").instance()
else:
splitter = preload("res://addons/de.mewin.gduibasics/scenes/flexview_hspliter.tscn").instance()
parent.add_child_below_node(self, splitter)
parent.remove_child(self)
GDBUIUtility.copy_size(splitter, self)
# add self first (if applicable)
if self_first:
splitter.add_child(self)
# create new flex container for other half
var new_container : Control = load("res://addons/de.mewin.gduibasics/scenes/flexview.tscn").instance()
new_container.add_view(title, view)
__copy_signal_handlers(new_container)
splitter.add_child(new_container)
# add self second (if applicable)
if !self_first:
splitter.add_child(self)
if placement == SplitLocation.TOP || placement == SplitLocation.BOTTOM:
splitter.split_offset = 0.5 * splitter.rect_size.y
else:
splitter.split_offset = 0.5 * splitter.rect_size.x
splitter.clamp_split_offset()
return new_container
################
# static stuff #
################
static func get_view_title_from_control(control : Control) -> String:
var prop = control.get("flexview_title")
if prop is String:
return prop
var title = control.get_meta(META_VIEW_TITLE)
if title is String:
return title
return control.name
static func get_all_flex_views() -> Array:
return GDBUtility.get_scene_tree().get_nodes_in_group(GROUP_NAME)
static func get_all_views() -> Array:
var views := []
for flex_view in get_all_flex_views():
views.append_array(flex_view.get_views())
return views
static func get_top_flex_view() -> Control:
# can't use GDBUtility.find_node_by_type as we cannot use our own type
var all_views := get_all_flex_views()
var top_view : Control = null
for flex_view in all_views:
if top_view == null || top_view.is_greater_than(flex_view):
top_view = flex_view
return top_view
static func add_view_anywhere(title : String, view : Control, make_current := true) -> Control:
var flex_view := _get_last_drop_target()
if flex_view == null:
flex_view = get_top_flex_view()
if flex_view != null:
flex_view.add_view(title, view, make_current)
return flex_view
static func add_unique_view_anywhere(title : String, view : Control, type : String, key, make_current := true) -> Control:
var flex_view := add_view_anywhere(title, view, make_current)
if flex_view == null: # failed
return null
view.set_meta(META_UNIQUE_TYPE, type)
view.set_meta(META_UNIQUE_KEY, key)
return flex_view
static func remove_all_views_anywhere() -> void:
for flex_view in get_all_flex_views():
flex_view.remove_all_views()
static func find_unique_view(type : String, key) -> Control:
for view_ctrl in get_all_views():
if view_ctrl.has_meta(META_UNIQUE_TYPE) && view_ctrl.has_meta(META_UNIQUE_KEY)\
&& view_ctrl.get_meta(META_UNIQUE_TYPE) == type && view_ctrl.get_meta(META_UNIQUE_KEY) == key:
return view_ctrl
return null
static func find_flex_view(view_control : Control) -> Control:
var my_script : Script = load("res://addons/de.mewin.gduibasics/scripts/types/controls/flexview.gd")
return GDBUtility.find_parent_with_type(view_control, my_script) as Control
static func make_view_current(view_control : Control) -> void:
var flex_view := find_flex_view(view_control)
if flex_view:
flex_view.current_view = view_control.get_position_in_parent()
static func _set_last_drop_target(flex_view : Control) -> void:
flex_view.get_tree().set_meta(META_LAST_DROP_TARGET, flex_view.get_path())
static func _get_last_drop_target() -> Control:
var tree := GDBUtility.get_scene_tree()
if tree.has_meta(META_LAST_DROP_TARGET):
var flex_view_path := tree.get_meta(META_LAST_DROP_TARGET) as NodePath
if flex_view_path:
var flex_view := tree.root.get_node_or_null(flex_view_path) as Control
return flex_view
return null
#################
# private stuff #
#################
func _start_dropping() -> void:
__drop_position = -1
_drop_indicator.visible = true
_tween.stop_all()
_tween.interpolate_property(_drop_indicator, "modulate", Color.transparent, Color.white, INDICATOR_ANIM_DURATION)
_tween.start()
set_process(true)
func _stop_dropping() -> void:
_tween.stop_all()
_tween.interpolate_property(_drop_indicator, "modulate", Color.white, Color.transparent, INDICATOR_ANIM_DURATION)
_tween.start()
set_process(false)
yield(get_tree().create_timer(INDICATOR_ANIM_DURATION), "timeout")
if !is_processing(): # mOybe we are dragging again
_drop_indicator.visible = false
func _drop(view : Control) -> void:
_stop_dropping()
var title := get_view_title_from_control(view)
if __drop_position != DropPosition.CENTER:
var new_container := split_view(__drop_position, title, view)
_set_last_drop_target(new_container)
else:
add_view(title, view)
_set_last_drop_target(self)
func __make_floating_tab(tab : int) -> void:
var window : WindowDialog = load("res://addons/de.mewin.gduibasics/scenes/flexview_window.tscn").instance()
get_tree().root.add_child(window)
# remove tab
var tab_ctrl : Control = $tabs.get_tab_control(tab)
remove_view(tab)
# add to new window
var title = get_view_title_from_control(tab_ctrl)
window.flex_view.add_view(title, tab_ctrl)
__copy_signal_handlers(window.flex_view)
# place window
var vp_size := get_viewport().size
var mouse_pos := get_viewport().get_mouse_position()
var win_size := vp_size * Vector2(0.5, 0.5)
var min_size := window.get_combined_minimum_size()
var title_height := window.get_constant("title_height")
window.rect_size = Vector2(max(win_size.x, min_size.x), max(win_size.y, min_size.y))
window.rect_position = mouse_pos - Vector2(0.5 * window.rect_size.x, -0.5 * title_height)
window.visible = true
# emulate mouse down for dragging the window
var event := InputEventMouseButton.new()
event.button_index = BUTTON_LEFT
event.pressed = true
event.button_mask = BUTTON_MASK_LEFT
event.position = mouse_pos
event.global_position = mouse_pos
Input.parse_input_event(event)
func __get_drop_rect(drop_position : int) -> Rect2:
var full_rect := Rect2(Vector2(), rect_size)
match drop_position:
DropPosition.TOP:
return full_rect.grow_individual(0.0, 0.0, 0.0, -0.5 * full_rect.size.y)
DropPosition.BOTTOM:
return full_rect.grow_individual(0.0, -0.5 * full_rect.size.y, 0.0, 0.0)
DropPosition.LEFT:
return full_rect.grow_individual(0.0, 0.0, -0.5 * full_rect.size.x, 0.0)
DropPosition.RIGHT:
return full_rect.grow_individual(-0.5 * full_rect.size.x, 0.0, 0.0, 0.0)
return full_rect
func __copy_signal_handlers(other_view : Control) -> void:
GDBUtility.copy_all_signal_handlers(other_view, self, __HANDLERS_TO_COPY)
#####################
# setters & getters #
#####################
func _set_current_view(val : int) -> void:
$tabs.current_tab = val
emit_signal("current_view_changed", self)
func _get_current_view() -> int:
return $tabs.current_tab
func _set_show_tabs(show : bool) -> void:
show_tabs = show
if $tabs:
$tabs.show_tabs = show
###################
# signal handlers #
###################
func _on_tabs_tab_dragged_out(tab : int, position : Vector2) -> void:
__make_floating_tab(tab)
func _on_tabs_tab_close(tab : int):
close_view(tab)
func _on_flexview_title_changed(control : Control) -> void:
var view := find_view(control)
var title = control.get("flexview_title")
if view >= 0 && title is String:
set_view_title(view, title)
func _on_flexview_close_me(control : Control) -> void:
var view := find_view(control)
if view >= 0:
remove_view(view)
###########
# signals #
###########
signal view_added(view, flex_view)
signal view_removed(view, flex_view)
signal view_closing(view, flex_view)
signal current_view_changed(flex_view)

View File

@ -0,0 +1,38 @@
extends SplitContainer
#############
# overrides #
#############
func add_child(child : Node, legible_unique_name := false) -> void:
.add_child(child, legible_unique_name)
child.connect("tree_exited", self, "_on_child_tree_exited")
#################
# private stuff #
#################
func __try_collapse() -> void:
var count := get_child_count()
if count == 0: # nothing left? I can go now
self.queue_free()
return
elif count > 1: # more than one? I have to stay
return
# exactly one? replace me with them
var child := get_child(0)
remove_child(child)
get_parent().add_child_below_node(self, child)
GDBUIUtility.copy_size(child, self)
self.queue_free()
###################
# signal handlers #
###################
func _on_child_tree_exited() -> void:
if !get_tree():
return
yield(get_tree(), "idle_frame") # wait until child is actually gone
__try_collapse()

View File

@ -0,0 +1,141 @@
extends WindowDialog
const FlexView = preload("res://addons/de.mewin.gduibasics/scenes/scripts/flexview.gd")
var flex_view : FlexView setget _set_flex_view, _get_flex_view
var __drop_target : FlexView = null
var __is_dragging := false
#############
# overrides #
#############
func _ready() -> void:
set_process(false) # only used to stop dropping when mouse button is lifted
__update_title()
func _process(delta : float) -> void:
if !Input.is_mouse_button_pressed(BUTTON_LEFT):
__drop()
func _input(event : InputEvent) -> void:
if event is InputEventKey && event.scancode == KEY_SHIFT && __is_dragging:
__update_dragging()
#################
# private stuff #
#################
func __update_view_count() -> void:
var child := __get_flex_child()
if !(child is FlexView):
return
var count : int = child.get_view_count()
if count == 0:
self.queue_free()
return
else:
# child.show_tabs = (count > 1)
pass
func __update_dragging() -> void:
var child := __get_flex_child()
if !(child is FlexView) || child.get_view_count() > 1:
set_process(false)
__is_dragging = false
return
__is_dragging = true
set_process(true)
# dragging with shift disables dropping
if Input.is_key_pressed(KEY_SHIFT):
__set_drop_target(null)
return
var mouse_pos := get_viewport().get_mouse_position()
var flex_views := get_tree().get_nodes_in_group(FlexView.GROUP_NAME)
var views_under_cursor := []
for view in flex_views:
if !self.is_a_parent_of(view) && view.visible && view.get_global_rect().has_point(mouse_pos):
views_under_cursor.append(view)
if views_under_cursor.empty():
__set_drop_target(null)
return
# find first in tree
var target : FlexView = views_under_cursor[0]
for i in range(1, views_under_cursor.size()):
var node : FlexView = views_under_cursor[i]
if GDBUtility.control_is_in_front_of(node, target):
target = node
__set_drop_target(target)
func __set_drop_target(target : FlexView) -> void:
if target == __drop_target:
return
set_process(is_instance_valid(__drop_target)) # process to check if mouse button is lifted
if is_instance_valid(__drop_target):
__drop_target._stop_dropping()
__drop_target = target
if is_instance_valid(__drop_target):
__drop_target._start_dropping()
func __drop() -> void:
var child := __get_flex_child()
set_process(false)
__is_dragging = false
if !is_instance_valid(__drop_target) || !(child is FlexView) || child.get_view_count() != 1:
return
var control : Control = child.get_view_control(0)
child.remove_view(0)
__drop_target._drop(control)
self.queue_free()
func __update_title() -> void:
var child := __get_flex_child()
if !(child is FlexView):
window_title = ""
elif child.get_view_count() > 0:
window_title = child.get_view_title(child.current_view)
func __get_flex_child() -> Control:
return get_child(1) as Control # TODO: something better (and safer) than a fixed index
func __close_requested() -> void:
for flex_view in GDBUtility.find_nodes_by_type(self, FlexView):
flex_view.close_all_views()
#####################
# setters & getters #
#####################
func _set_flex_view(val) -> void:
assert(0, "Cannot set this.")
func _get_flex_view() -> FlexView:
return $flex_view as FlexView
###################
# signal handlers #
###################
func _on_flex_view_view_added(view : int, flex_view : FlexView) -> void:
__update_view_count()
func _on_flex_view_view_removed(view : int, flex_view : FlexView) -> void:
__update_view_count()
func _on_flexview_window_item_rect_changed() -> void:
__update_dragging()
func _on_flex_view_current_view_changed(flex_view : FlexView):
__update_title()
func _on_flexview_window_visibility_changed():
if !visible:
__close_requested()
visible = true

View File

@ -0,0 +1,100 @@
extends Popup
enum Position {
ABOVE,
BELOW
}
var line_edit : LineEdit setget _set_line_edit
export(Position) var position = Position.BELOW
#############
# overrides #
#############
func _ready():
call_deferred("__setup")
#################
# private stuff #
#################
func __setup():
__connect()
func __connect():
if !line_edit:
return
line_edit.connect("focus_entered", self, "_on_line_edit_focus_entered")
line_edit.connect("focus_exited", self, "_on_line_edit_focus_exited")
line_edit.connect("item_rect_changed", self, "_on_line_edit_item_rect_changed")
line_edit.connect("resized", self, "_on_line_edit_resized")
func __disconnect():
if !line_edit:
return
line_edit.disconnect("focus_entered", self, "_on_line_edit_focus_entered")
line_edit.disconnect("focus_exited", self, "_on_line_edit_focus_exited")
line_edit.disconnect("item_rect_changed", self, "_on_line_edit_item_rect_changed")
line_edit.disconnect("resized", self, "_on_line_edit_resized")
func __calc_rect() -> Rect2:
var position_ = position
var pos := line_edit.rect_global_position
var vp_pos = get_viewport_rect().position
var vp_size = get_viewport_rect().size
var size := rect_min_size
var le_size := line_edit.rect_size
var max_height : float
if pos.y + size.y > vp_size.y:
position_ = Position.ABOVE
elif pos.y < vp_pos.y:
position_ = Position.BELOW
if position_ == Position.ABOVE:
max_height = pos.y - vp_pos.y
else:
max_height = (vp_pos.y + vp_size.y) - (pos.y + size.y)
var res_size = Vector2(le_size.x, 100.0)
if position_ == Position.ABOVE:
return Rect2(pos - Vector2(0.0, res_size.y), res_size)
else:
return Rect2(pos + Vector2(0.0, size.y), res_size)
func __update():
var should_be_visible = line_edit.has_focus()
if !should_be_visible:
hide()
else:
var rect := __calc_rect()
if visible:
rect_position = rect.position
rect_size = rect.size
else:
popup(rect)
############
# handlers #
############
func _on_line_edit_focus_entered():
__update()
func _on_line_edit_focus_exited():
hide()
func _on_line_edit_item_rect_changed():
__update()
func _on_line_edit_resized():
__update()
###########
# setters #
###########
func _set_line_edit(value : LineEdit):
if value != line_edit:
__disconnect()
line_edit = value
__connect()

View File

@ -0,0 +1,47 @@
tool
extends ConfirmationDialog
export(bool) var allow_empty := true setget _set_allow_empty
################
# public stuff #
################
func get_line_edit() -> LineEdit:
return $container/line_edit as LineEdit
#################
# private stuff #
#################
func __update():
var valid := true
if !allow_empty && !$container/line_edit.text:
valid = false
get_ok().disabled = !valid
###########
# setters #
###########
func _set_allow_empty(value : bool):
if value != allow_empty:
allow_empty = value
__update()
############
# handlers #
############
func _on_line_input_dialog_about_to_show():
__update()
func _on_line_edit_text_changed(new_text : String):
__update()
func _on_line_edit_text_entered(new_text):
if !get_ok().disabled:
visible = false
emit_signal("confirmed")
func _on_line_input_dialog_visibility_changed():
if visible:
yield(get_tree(), "idle_frame")
$container/line_edit.grab_focus()
$container/line_edit.caret_position = $container/line_edit.text.length()

View File

@ -0,0 +1,5 @@
tool
extends ConfirmationDialog
func get_editor_node_tree() -> UIB_EditorNodeTree:
return $tree as UIB_EditorNodeTree

View File

@ -0,0 +1,174 @@
extends PopupPanel
class_name SelectorPopup
class _Item:
var label : String
var icon : Texture
var metadata
var current_list_index := -1
func _init(label_ : String, icon_ : Texture, metadata_):
self.label = label_
self.icon = icon_
self.metadata = metadata_
export var match_case := false
onready var _ledit_search : LineEdit = GDBUtility.find_node_by_name(self, "ledit_search")
onready var _ilist_content : ItemList = GDBUtility.find_node_by_name(self, "ilist_content")
var _items := []
var __needs_update := false
var __caret := 0
var __submitted := false
################
# overridables #
################
func _should_show(item : _Item) -> bool:
var search_text := _ledit_search.text.strip_edges()
if search_text == "":
return true
var item_text := item.label
if !match_case:
search_text = search_text.to_lower()
item_text = item_text.to_lower()
return search_text in item_text
################
# public stuff #
################
func add_item(label : String, icon : Texture = null, metadata = null) -> void:
_items.append(_Item.new(label, icon, metadata))
invalidate()
func clear_items() -> void:
_items.clear()
invalidate()
func get_item_label(idx : int) -> String:
if idx < 0 || idx >= _items.size():
return ""
return _items[idx].label
func get_item_metadata(idx : int):
if idx < 0 || idx >= _items.size():
return null
return _items[idx].metadata
func invalidate() -> void:
# only updates once if multiple calls are done in a single frame
__needs_update = true
yield(get_tree(), "idle_frame")
if __needs_update:
__needs_update = false
__update()
#################
# private stuff #
#################
func __update() -> void:
var idx_increment := 0
var insert_index := 0
for item in self._items:
var should_show := self._should_show(item)
var is_visible : bool = (item.current_list_index > -1)
if is_visible:
item.current_list_index += idx_increment
if should_show && !is_visible:
self.__add_item(item, insert_index)
item.current_list_index = insert_index
idx_increment += 1
elif !should_show && is_visible:
_ilist_content.remove_item(item.current_list_index)
item.current_list_index = -1
idx_increment -= 1
if should_show:
insert_index += 1
if !_ilist_content.is_anything_selected() && _ilist_content.get_item_count() > 0:
__select(0)
func __add_item(item : _Item, insert_index : int) -> void:
var new_idx := _ilist_content.get_item_count()
_ilist_content.add_item(item.label, item.icon)
_ilist_content.set_item_metadata(new_idx, item)
if insert_index != new_idx:
_ilist_content.move_item(new_idx, insert_index)
func __select(idx : int) -> void:
_ilist_content.select(idx)
_ilist_content.ensure_current_is_visible()
func __select_next() -> void:
var selected := _ilist_content.get_selected_items()
var idx := 0
if !selected.empty():
idx = selected[0] + 1
if idx >= _ilist_content.get_item_count():
return
__select(idx)
func __select_prev() -> void:
var selected := _ilist_content.get_selected_items()
var idx := 0
if !selected.empty():
idx = selected[0] - 1
if idx < 0:
return
__select(idx)
func __item_from_list_index(idx : int) -> _Item:
return _ilist_content.get_item_metadata(idx) as _Item
func __item_index(itm : _Item) -> int:
return _items.find(itm)
###################
# signal handlers #
###################
func _on_selector_popup_about_to_show() -> void:
__submitted = false
_ledit_search.clear()
_ledit_search.grab_focus()
_ilist_content.unselect_all()
__update()
func _on_ledit_search_text_changed(new_text : String) -> void:
__update()
func _on_ledit_search_gui_input(event : InputEvent) -> void:
if !event.is_pressed():
return
if event.is_action("ui_down"):
__select_next()
elif event.is_action("ui_up"):
__select_prev()
else:
__caret = _ledit_search.caret_position
return
_ledit_search.caret_position = __caret
func _on_ledit_search_text_entered(new_text : String) -> void:
var selected := _ilist_content.get_selected_items()
if selected.empty():
return
var idx := __item_index(__item_from_list_index(selected[0]))
__submitted = true
emit_signal("selected", idx)
hide()
func _on_selector_popup_popup_hide() -> void:
if !__submitted:
emit_signal("selected", -1)
###########
# signals #
###########
signal selected(index)

View File

@ -0,0 +1,77 @@
tool
extends Control
enum Type {
STRING,
INT,
REAL
}
onready var edt_text := $edt_text
onready var spin_number := $spin_number
export var value = "" setget _set_value
export(Type) var type = Type.STRING setget _set_type
func _ready():
__setup_type()
func __setup_type():
match type:
Type.STRING:
edt_text.visible = true
Type.INT, Type.REAL:
spin_number.visible = true
_set_value(convert(value, __type_id()))
__fill_value()
emit_signal("value_changed")
func __fill_value():
match type:
Type.STRING:
edt_text.text = str(value)
Type.INT, Type.REAL:
spin_number.value = float(value)
func __type_id() -> int:
match type:
Type.STRING:
return TYPE_STRING
Type.INT:
return TYPE_INT
Type.REAL:
return TYPE_REAL
_:
assert(false)
return TYPE_STRING
func _set_value(val):
val = convert(val, __type_id())
if typeof(value) != __type_id() || value != val:
value = val
__fill_value()
func _set_type(value : int):
if value != type:
type = value
for child in get_children():
child.visible = false
__setup_type()
func _on_edt_text_text_changed(new_text : String):
assert(type == Type.STRING)
if !value is String || value != new_text:
value = new_text
emit_signal("value_changed")
func _on_spin_number_value_changed(value_ : float):
assert(type == Type.INT || type == Type.REAL)
if type == Type.INT:
value_ = int(value_)
if typeof(value) != __type_id() || value != value_:
value = value_
emit_signal("value_changed")
signal value_changed()

View File

@ -0,0 +1,40 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/selector_popup.gd" type="Script" id=1]
[node name="selector_popup" type="PopupPanel"]
margin_right = 139.0
margin_bottom = 198.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="vbox_main" type="VBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 5.0
margin_top = 5.0
margin_right = -5.0
margin_bottom = -5.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ledit_search" type="LineEdit" parent="vbox_main"]
margin_right = 129.0
margin_bottom = 22.0
focus_neighbour_top = NodePath(".")
focus_neighbour_bottom = NodePath(".")
[node name="ilist_content" type="ItemList" parent="vbox_main"]
margin_top = 26.0
margin_right = 129.0
margin_bottom = 188.0
size_flags_vertical = 3
[connection signal="about_to_show" from="." to="." method="_on_selector_popup_about_to_show"]
[connection signal="popup_hide" from="." to="." method="_on_selector_popup_popup_hide"]
[connection signal="gui_input" from="vbox_main/ledit_search" to="." method="_on_ledit_search_gui_input"]
[connection signal="text_changed" from="vbox_main/ledit_search" to="." method="_on_ledit_search_text_changed"]
[connection signal="text_entered" from="vbox_main/ledit_search" to="." method="_on_ledit_search_text_entered"]

View File

@ -0,0 +1,34 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/de.mewin.gduibasics/scenes/scripts/variant_editor.gd" type="Script" id=1]
[node name="variant_editor" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_right = -617.0
margin_bottom = -576.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="edt_text" type="LineEdit" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
margin_bottom = -24.0
size_flags_horizontal = 3
caret_blink = true
caret_blink_speed = 0.5
__meta__ = {
"_edit_use_anchors_": false
}
[node name="spin_number" type="SpinBox" parent="."]
visible = false
anchor_right = 1.0
margin_bottom = 24.0
__meta__ = {
"_edit_use_anchors_": false
}
[connection signal="text_changed" from="edt_text" to="." method="_on_edt_text_text_changed"]
[connection signal="value_changed" from="spin_number" to="." method="_on_spin_number_value_changed"]

55
scripts/libs/ui_util.gd Normal file
View File

@ -0,0 +1,55 @@
extends Node
class_name GDBUIUtility
static func uncollapse_tree_item(tree_item : TreeItem):
while tree_item:
tree_item.collapsed = false
tree_item = tree_item.get_parent()
static func find_scene_tree_editor(root : Node = null) -> Node:
if not root:
root = (Engine.get_main_loop() as SceneTree).root
if root.get_class() == "SceneTreeEditor":
return root
for child in root.get_children():
var scene_tree_editor := find_scene_tree_editor(child)
if scene_tree_editor:
return scene_tree_editor
return null
static func new_item_sorted(tree : Tree, parent : TreeItem, key : String, meta_column := 0) -> TreeItem:
var item := parent.get_children()
var found_itm : TreeItem
var idx := 0
while true:
if item == null:
break
var itm_key := item.get_metadata(meta_column) as String
if itm_key == key:
return item
elif itm_key > key:
break
item = item.get_next()
idx += 1
# not found
var new_itm : TreeItem = tree.create_item(parent, idx)
new_itm.set_metadata(meta_column, key)
return new_itm
static func copy_size(target : Control, source : Control) -> void:
target.rect_min_size = source.rect_min_size
target.rect_size = source.rect_size
target.size_flags_horizontal = source.size_flags_horizontal
target.size_flags_vertical = source.size_flags_vertical
target.size_flags_stretch_ratio = source.size_flags_stretch_ratio
target.anchor_top = source.anchor_top
target.anchor_bottom = source.anchor_bottom
target.anchor_left = source.anchor_left
target.anchor_right = source.anchor_right
target.margin_top = source.margin_top
target.margin_bottom = source.margin_bottom
target.margin_left = source.margin_left
target.margin_right = source.margin_right

77
scripts/libs/ux.gd Normal file
View File

@ -0,0 +1,77 @@
# various UX stuff
extends Object
class_name GDBUX
const __META_STATE = "__gdbui_ux_state__"
class __State:
signal recent_places_changed()
signal favourite_places_changed()
################
# public stuff #
################
static func get_recent_places() -> Array:
return GDBSettings.get_value(GDBConstants.SETTING_RECENT_PLACES, [])
static func add_recent_place(place) -> void:
if place is UIB_FileAdapter.FileEntry:
add_recent_place(place._get_path())
return
assert(place is String)
var recent_places : Array = GDBSettings.get_value(GDBConstants.SETTING_RECENT_PLACES, [])
recent_places.erase(place)
recent_places.push_front(place)
GDBSettings.set_value(GDBConstants.SETTING_RECENT_PLACES, recent_places)
__get_state().emit_signal("recent_places_changed")
static func get_favourite_places() -> Array:
return GDBSettings.get_value(GDBConstants.SETTING_FAVOURITE_PLACES, [])
static func is_favourite_place(place) -> bool:
if place is UIB_FileAdapter.FileEntry:
return is_favourite_place(place._get_path())
assert(place is String)
return GDBSettings.array_has_value(GDBConstants.SETTING_FAVOURITE_PLACES, place)
static func add_favourite_place(place) -> void:
if place is UIB_FileAdapter.FileEntry:
add_favourite_place(place._get_path())
return
assert(place is String)
var places = GDBSettings.get_value(GDBConstants.SETTING_FAVOURITE_PLACES, [])
if !places.has(place):
places.append(place)
GDBSettings.set_value(GDBConstants.SETTING_FAVOURITE_PLACES, places)
__get_state().emit_signal("favourite_places_changed")
static func remove_favourite_place(place) -> void:
if place is UIB_FileAdapter.FileEntry:
remove_favourite_place(place._get_path())
return
assert(place is String)
var places = GDBSettings.get_value(GDBConstants.SETTING_FAVOURITE_PLACES, [])
if places.has(place):
places.erase(place)
GDBSettings.set_value(GDBConstants.SETTING_FAVOURITE_PLACES, places)
__get_state().emit_signal("favourite_places_changed")
static func connect_static(sig_name : String, receiver : Object, method : String, binds := []) -> int:
return __get_state().connect(sig_name, receiver, method, binds)
static func disconnect_static(sig_name : String, receiver : Object, method : String) -> void:
__get_state().disconnect(sig_name, receiver, method)
#################
# private stuff #
#################
static func __get_state() -> __State:
return GDBUtility.get_state_object(__META_STATE, __State)

View File

@ -0,0 +1,82 @@
extends Button
class_name UIB_ActionButton
var action : UIB_Action setget _set_action
#############
# overrides #
#############
func _ready():
if !action:
_set_action(GDBUtility.find_node_by_type(self, UIB_Action))
if !action:
var action_reference : UIB_ActionReference = GDBUtility.find_node_by_type(self, UIB_ActionReference)
if action_reference:
_set_action(action_reference.get_action())
func _pressed():
if action:
action._apply()
#################
# private stuff #
#################
func __connect():
if !action:
return
action.connect("disabled_changed", self, "_on_action_disabled_changed")
action.connect("toggled_changed", self, "_on_action_toggled_changed")
action.connect("text_changed", self, "_on_action_text_changed")
action.connect("icon_changed", self, "_on_action_icon_changed")
func __disconnect():
if !action:
return
action.disconnect("disabled_changed", self, "_on_action_disabled_changed")
action.disconnect("toggled_changed", self, "_on_action_toggled_changed")
action.disconnect("text_changed", self, "_on_action_text_changed")
action.disconnect("icon_changed", self, "_on_action_icon_changed")
############
# handlers #
############
func _on_action_disabled_changed():
disabled = action._is_disabled()
func _on_action_toggled_changed():
toggle_mode = action._is_toggled()
pressed = action._is_toggled()
func _on_action_text_changed():
if !icon:
text = action._get_text()
hint_tooltip = action._get_text()
func _on_action_icon_changed():
icon = action._get_icon()
_on_action_text_changed() # update hint/text
###########
# setters #
###########
func _set_action(value : UIB_Action):
if value != action:
__disconnect()
action = value
if action:
icon = action._get_icon()
if !icon:
text = action._get_text()
hint_tooltip = action._get_text()
disabled = action._is_disabled()
toggle_mode = action._is_toggled()
pressed = action._is_toggled()
__connect()
else:
icon = null
text = ""
hint_tooltip = ""
disabled = true
toggle_mode = false
pressed = false

View File

@ -0,0 +1,57 @@
extends MenuButton
class_name UIB_ActionMenuButton
onready var __popup := get_popup()
#############
# overrides #
#############
func _ready():
for child in get_children():
if child is UIB_Action:
add_action(child)
elif child is UIB_Seperator:
add_seperator(child)
elif child.has_method("get_action"):
add_action(child.get_action())
connect("about_to_show", self, "_on_about_to_show")
__popup.connect("index_pressed", self, "_on_popup_index_pressed")
################
# public stuff #
################
func add_action(action : UIB_Action):
if !action:
return
var idx = __popup.get_item_count()
__popup.add_item(action._get_text())
__popup.set_item_metadata(idx, action)
func add_seperator(seperator : UIB_Seperator):
__popup.add_separator(seperator.text)
############
# handlers #
############
func _on_about_to_show():
for i in range(__popup.get_item_count()):
var action := __popup.get_item_metadata(i) as UIB_Action
if !action:
continue
action._update()
__popup.set_item_text(i, action._get_text())
__popup.set_item_icon(i, action._get_icon())
__popup.set_item_shortcut(i, action._get_shortcut())
__popup.set_item_disabled(i, action._is_disabled())
__popup.set_item_as_checkable(i, action._is_toggleable())
__popup.set_item_checked(i, action._is_toggled())
func _on_popup_index_pressed(index : int):
var action := __popup.get_item_metadata(index) as UIB_Action
if !action:
return
action._apply()

View File

@ -0,0 +1,50 @@
extends PopupMenu
class_name UIB_ActionPopupMenu
#############
# overrides #
#############
func _ready():
for child in get_children():
if child is UIB_Action:
add_action(child)
elif child.has_method("get_action"):
add_action(child.get_action())
connect("about_to_show", self, "_on_about_to_show")
connect("index_pressed", self, "_on_popup_index_pressed")
################
# public stuff #
################
func add_action(action : UIB_Action):
if !action:
return
var idx = get_item_count()
add_item(action._get_text())
set_item_metadata(idx, action)
############
# handlers #
############
func _on_about_to_show():
for i in range(get_item_count()):
var action := get_item_metadata(i) as UIB_Action
if !action:
continue
action._update()
set_item_text(i, action._get_text())
set_item_icon(i, action._get_icon())
set_item_shortcut(i, action._get_shortcut())
set_item_disabled(i, action._is_disabled())
set_item_as_checkable(i, action._is_toggleable())
set_item_checked(i, action._is_toggled())
func _on_popup_index_pressed(index : int):
var action := get_item_metadata(index) as UIB_Action
if !action:
return
action._apply()

View File

@ -0,0 +1,29 @@
extends BoxContainer
class_name UIB_ActionToolbar
#############
# overrides #
#############
func _ready():
for child in get_children():
if child is UIB_Action:
add_action(child)
elif child.has_method("get_action"):
add_action(child.get_action())
################
# public stuff #
################
func add_action(action : UIB_Action):
var btn = ToolButton.new()
btn.set_script(preload("res://addons/de.mewin.gduibasics/scripts/types/controls/action_button.gd"))
btn.action = action
call_deferred("add_child", btn)
func remove_action(action : UIB_Action):
for child in get_children():
if child == action || child.get("action") == action \
|| (child is UIB_ActionReference && child.get_action() == action):
remove_child(child)
child.queue_free()

View File

@ -0,0 +1,250 @@
tool
extends MarginContainer
class_name UIB_BorderContainer
enum __MouseMode {
NONE,
MOVING,
RESIZE_L,
RESIZE_R,
RESIZE_T,
RESIZE_B,
RESIZE_TL,
RESIZE_TR,
RESIZE_BL,
RESIZE_BR
}
export var resizable := true
export var show_title := false setget _set_show_title
export var title := "" setget _set_title
export var grab_everywhere := false
export(Texture) var title_image = null
export(StyleBox) var style_left setget _set_style_left
export(StyleBox) var style_right setget _set_style_right
export(StyleBox) var style_top setget _set_style_top
export(StyleBox) var style_bottom setget _set_style_bottom
var __mouse_mode = __MouseMode.NONE
var __mouse_down_pos : Vector2
#############
# overrides #
#############
func _gui_input(event):
if event is InputEventMouseButton && event.pressed && event.button_index == BUTTON_LEFT:
if resizable:
__mouse_mode = __resize_mode(event.position)
if __mouse_mode != __MouseMode.NONE:
__mouse_down_pos = _win_get_mouse_position() - _win_get_position()
if __mouse_mode == __MouseMode.RESIZE_B || __mouse_mode == __MouseMode.RESIZE_BL || __mouse_mode == __MouseMode.RESIZE_BR:
__mouse_down_pos.y -= _win_get_size().y
if __mouse_mode == __MouseMode.RESIZE_R || __mouse_mode == __MouseMode.RESIZE_TR || __mouse_mode == __MouseMode.RESIZE_BR:
__mouse_down_pos.x -= _win_get_size().x
set_process(true)
return
if grab_everywhere || event.position.y < __get_margin("top"):
__mouse_mode = __MouseMode.MOVING
__mouse_down_pos = _win_get_mouse_position() - _win_get_position()
set_process(true)
accept_event()
elif event is InputEventMouseMotion && resizable:
match __resize_mode(event.position):
__MouseMode.RESIZE_L, __MouseMode.RESIZE_R:
mouse_default_cursor_shape = CURSOR_HSIZE
__MouseMode.RESIZE_T, __MouseMode.RESIZE_B:
mouse_default_cursor_shape = CURSOR_VSIZE
__MouseMode.RESIZE_TL, __MouseMode.RESIZE_BR:
mouse_default_cursor_shape = CURSOR_FDIAGSIZE
__MouseMode.RESIZE_TR, __MouseMode.RESIZE_BL:
mouse_default_cursor_shape = CURSOR_BDIAGSIZE
_:
mouse_default_cursor_shape = CURSOR_ARROW
func _process(delta):
if !Input.is_mouse_button_pressed(BUTTON_LEFT):
__mouse_mode = __MouseMode.NONE
if __mouse_mode == __MouseMode.NONE:
set_process(false)
return
if __mouse_mode == __MouseMode.MOVING:
_win_set_position(_win_get_mouse_position() - __mouse_down_pos)
return
var win_pos := _win_get_position()
var win_size := _win_get_size()
var win_min_size := _win_get_minimum_size()
var mouse_pos := _win_get_mouse_position()
if __mouse_mode == __MouseMode.RESIZE_L || __mouse_mode == __MouseMode.RESIZE_TL || __mouse_mode == __MouseMode.RESIZE_BL:
var global_right := win_pos.x + win_size.x
var x_pos := mouse_pos.x - __mouse_down_pos.x
x_pos = min(x_pos, win_pos.x + win_size.x - win_min_size.x)
win_pos.x = x_pos
win_size.x = global_right - win_pos.x
elif __mouse_mode == __MouseMode.RESIZE_R || __mouse_mode == __MouseMode.RESIZE_TR || __mouse_mode == __MouseMode.RESIZE_BR:
var x_pos := mouse_pos.x - __mouse_down_pos.x
win_size.x = x_pos - win_pos.x
win_size.x = max(win_size.x, win_min_size.x)
if __mouse_mode == __MouseMode.RESIZE_T || __mouse_mode == __MouseMode.RESIZE_TL || __mouse_mode == __MouseMode.RESIZE_TR:
var global_bottom := win_pos.y + win_size.y
var y_pos := mouse_pos.y - __mouse_down_pos.y
y_pos = min(y_pos, win_pos.y + win_size.y - win_min_size.y)
win_pos.y = y_pos
win_size.y = global_bottom - win_pos.y
elif __mouse_mode == __MouseMode.RESIZE_B || __mouse_mode == __MouseMode.RESIZE_BL || __mouse_mode == __MouseMode.RESIZE_BR:
var y_pos := mouse_pos.y - __mouse_down_pos.y
win_size.y = y_pos - win_pos.y
win_size.y = max(win_size.y, win_min_size.y)
_win_set_position(win_pos)
_win_set_size(win_size)
func _draw():
var marg_left = __get_margin("left")
var marg_right = __get_margin("right")
var marg_top = __get_margin("top")
var marg_bottom = __get_margin("bottom")
if show_title:
var title_height := 0
if title_image:
# title_height = title_image.get_height()
title_height = 0.0
else:
var font := get_font("font", "Label")
title_height = font.get_height() + font.get_descent()
if marg_top < title_height:
marg_top = title_height
set("custom_constants/margin_top", marg_top)
if style_left:
var min_size = style_left.get_minimum_size()
min_size.x = max(min_size.x, marg_left)
draw_style_box(style_left, Rect2(Vector2(), Vector2(min_size.x, rect_size.y)))
if style_right:
var min_size = style_right.get_minimum_size()
min_size.x = max(min_size.x, marg_right)
draw_style_box(style_right, Rect2(Vector2(rect_size.x - min_size.x, 0.0), Vector2(min_size.x, rect_size.y)))
if style_top:
var min_size = style_top.get_minimum_size()
min_size.y = max(min_size.y, marg_top)
draw_style_box(style_top, Rect2(Vector2(), Vector2(rect_size.x, min_size.y)))
if style_bottom:
var min_size = style_bottom.get_minimum_size()
min_size.y = max(min_size.y, marg_bottom)
draw_style_box(style_bottom, Rect2(Vector2(0.0, rect_size.y - min_size.y), Vector2(rect_size.x, min_size.y)))
if show_title:
if title_image:
draw_texture(title_image, Vector2(0.5 * (rect_size.x - title_image.get_width()), 0.0))
else:
var font := get_font("font", "Label")
var string_size := font.get_string_size(title)
draw_string(font, Vector2(0.5 * (rect_size.x - string_size.x), string_size.y), title)
################
# overridables #
################
func _win_get_position() -> Vector2:
return get_parent().rect_global_position
func _win_get_size() -> Vector2:
return get_parent().rect_size
func _win_get_mouse_position() -> Vector2:
return get_viewport().get_mouse_position()
func _win_get_minimum_size() -> Vector2:
return get_parent().rect_min_size
func _win_set_position(pos : Vector2):
get_parent().rect_global_position = pos
func _win_set_size(size : Vector2):
get_parent().rect_size = size
#################
# private stuff #
#################
func __get_margin(what : String) -> int:
var marg = get("custom_constants/margin_%s" % what)
if !marg:
return 0
return marg
func __resize_mode(cursor_pos : Vector2):
var resize_left := false
var resize_right := false
var resize_top := false
var resize_bottom := false
if cursor_pos.y < __get_margin("bottom"):
resize_top = true
elif cursor_pos.y > rect_size.y - __get_margin("bottom"):
resize_bottom = true
if cursor_pos.x < __get_margin("left"):
resize_left = true
elif cursor_pos.x > rect_size.x - __get_margin("right"):
resize_right = true
if resize_top:
if resize_left:
return __MouseMode.RESIZE_TL
elif resize_right:
return __MouseMode.RESIZE_TR
else:
return __MouseMode.RESIZE_T
elif resize_bottom:
if resize_left:
return __MouseMode.RESIZE_BL
elif resize_right:
return __MouseMode.RESIZE_BR
else:
return __MouseMode.RESIZE_B
elif resize_left:
return __MouseMode.RESIZE_L
elif resize_right:
return __MouseMode.RESIZE_R
else:
return __MouseMode.NONE
###########
# setters #
###########
func _set_show_title(val : bool):
if val != show_title:
show_title = val
update()
func _set_title(val : String):
if val != title:
title = val
update()
func _set_style_left(val : StyleBox):
if val != style_left:
style_left = val
update()
func _set_style_right(val : StyleBox):
if val != style_right:
style_right = val
update()
func _set_style_top(val : StyleBox):
if val != style_top:
style_top = val
update()
func _set_style_bottom(val : StyleBox):
if val != style_bottom:
style_bottom = val
update()

View File

@ -0,0 +1,95 @@
tool
extends Container
class_name UIB_DynamicGridContainer
var __DEFAULT_CONSTRAINTS = UIB_DynamicGridConstraints.new()
export(int, 1, 100) var columns := 1 setget _set_columns
func _notification(what):
if what == NOTIFICATION_SORT_CHILDREN:
__sort_children()
func __get_constraints(node : Node) -> UIB_DynamicGridConstraints:
for child in node.get_children():
if child is UIB_DynamicGridConstraints:
return child
return __DEFAULT_CONSTRAINTS
func __sort_children():
var column_widths := []
var column_expand := []
for i in range(columns):
column_widths.append(0)
column_expand.append(0.0)
var rows := [[]]
var col := 0
var row_width := 0.0
for child in get_children():
if !child is Control || !child.visible:
continue
var constraints := __get_constraints(child)
if col + constraints.colspan > columns:
col = 0
row_width = 0.0
rows.append([])
rows[rows.size() - 1].append(child)
child.rect_size = child.get_combined_minimum_size()
column_widths[col] = max(column_widths[col], child.rect_size.x)
if child.size_flags_horizontal & Control.SIZE_EXPAND:
column_expand[col] = max(column_expand[col], child.size_flags_stretch_ratio)
row_width += child.rect_size.x
col = col + constraints.colspan
var full_width := 0.0
for col_width in column_widths:
full_width += col_width
var remaining_width := rect_size.x - full_width
if remaining_width > 0.0:
var combined_expand_ratio := 0.0
for expand in column_expand:
combined_expand_ratio += expand
if combined_expand_ratio > 0.0:
for c in range(columns):
column_widths[c] += remaining_width * (column_expand[c] / combined_expand_ratio)
var pos := Vector2()
for row in rows:
var row_height := 0.0
col = 0
for child in row:
var constraints := __get_constraints(child)
child.rect_position = pos
var width := 0.0
for i in range(min(constraints.colspan, columns - col)):
width += column_widths[col + i]
if child.size_flags_horizontal & Control.SIZE_FILL:
child.rect_size.x = width
pos.x += width
row_height = max(row_height, child.rect_size.y)
col = col + constraints.colspan
for child in row:
if child.size_flags_vertical & Control.SIZE_FILL:
child.rect_size.y = row_height
pos.y += row_height
pos.x = 0.0
rect_min_size.y = pos.y
func _set_columns(value : int):
if value != columns:
columns = value
__sort_children()

View File

@ -0,0 +1,57 @@
extends Control
class_name UIB_EditorInspectorCategory
var label := "" setget _set_label
var icon : Texture setget _set_icon
onready var bg_color := get_color("prop_category", "Editor") setget _set_bg_color
func _ready():
update()
func _draw():
draw_rect(Rect2(Vector2(), get_size()), bg_color)
var font := get_font("font", "Tree")
var hs := get_constant("hseparation", "Tree")
var w := font.get_string_size(label).x
if icon:
w += hs + icon.get_width()
var ofs := (get_size().x - w) / 2
if icon:
draw_texture(icon, Vector2(ofs, (get_size().y - icon.get_height()) / 2).floor())
ofs += hs + icon.get_width()
var color := get_color("font_color", "Tree");
draw_string(font, Vector2(ofs, font.get_ascent() + (get_size().y - font.get_height()) / 2).floor(), label, color, get_size().x)
func _get_minimum_size() -> Vector2:
var ms : Vector2
var font := get_font("font", "Tree");
ms.x = 1
ms.y = font.get_height()
# if (icon.is_valid()) {
# ms.height = MAX(icon->get_height(), ms.height);
# }
ms.y += get_constant("vseparation", "Tree")
return ms
func _set_label(value : String):
if value != label:
label = value
update()
func _set_icon(value : Texture):
if value != icon:
icon = value
update()
func _set_bg_color(value : Color):
if value != bg_color:
bg_color = value
update()

View File

@ -0,0 +1,203 @@
tool
extends Tree
class_name UIB_EditorNodeTree
var __tree : Tree
var selected_node : NodePath setget _set_selected_node
var node_type_filter = Node
export var show_properties := false
export var show_methods := false
#############
# overrides #
#############
func _ready():
assert(Engine.editor_hint)
update()
connect("visibility_changed", self, "_on_visiblity_changed")
connect("item_selected", self, "_on_item_selected")
################
# public stuff #
################
func update():
if !__tree:
var scene_tree_editor := GDBUIUtility.find_scene_tree_editor()
if !scene_tree_editor:
return
__tree = __find_tree(scene_tree_editor)
if !__tree:
return
__copy_from(__tree)
if !selected_node.is_empty():
var full_path := __full_path(selected_node)
if !full_path.is_empty():
__set_selected_node(full_path, get_root())
#################
# private stuff #
#################
func __copy_from(tree : Tree):
clear()
columns = tree.columns
__copy_items(tree.get_root())
func __copy_items(item : TreeItem, parent : TreeItem = null):
if !item:
return
var new_item := create_item(parent)
for i in range(columns):
new_item.set_text(i, item.get_text(i))
new_item.set_icon(i, item.get_icon(i))
new_item.set_metadata(i, item.get_metadata(i))
var meta0 = item.get_metadata(0)
if meta0 is NodePath:
if show_properties:
var prop_item := create_item(new_item)
prop_item.set_text(0, tr("Properties"))
__fill_node_path_properties(prop_item, meta0)
prop_item.collapsed = true
prop_item.set_selectable(0, false)
if show_methods:
var method_item := create_item(new_item)
method_item.set_text(0, tr("Methods"))
__fill_methods(method_item, meta0)
method_item.collapsed = true
method_item.set_selectable(0, false)
var node : Node = get_node(meta0)
if !node is node_type_filter:
new_item.set_selectable(0, false)
else:
new_item.set_selectable(0, false)
if !new_item.is_selectable(0):
new_item.set_custom_color(0, Color.darkgray)
__copy_items(item.get_children(), new_item)
__copy_items(item.get_next(), parent)
func __fill_node_path_properties(parent : TreeItem, node_path : NodePath):
assert(node_path.is_absolute())
var node := get_node_or_null(node_path)
if !node:
return
__fill_properties(parent, node.get_property_list(), node_path)
func __fill_properties(parent : TreeItem, properties : Array, parent_path : NodePath):
GDBAlgorithm.remove_if(properties, 'return prop.get("usage", PROPERTY_USAGE_EDITOR) & PROPERTY_USAGE_EDITOR == 0', ["prop"])
GDBAlgorithm.sort_by(properties, "name")
for prop in properties:
var prop_item := create_item(parent)
var node_path := NodePath(str(parent_path) + ":" + prop["name"])
prop_item.set_text(0, "%s : %s" % [prop["name"], GDBUtility.format_type(prop, "any")])
prop_item.set_metadata(0, node_path)
__fill_properties(prop_item, GDBUtility.get_type_property_list(prop), node_path)
prop_item.collapsed = true
func __fill_methods(parent : TreeItem, node_path : NodePath):
assert(node_path.is_absolute())
var node := get_node_or_null(node_path)
if !node:
return
var methods := node.get_method_list()
GDBAlgorithm.sort_by(methods, "name")
for method in methods:
var method_item := create_item(parent)
method_item.set_text(0, GDBUtility.format_method_signature(method))
func __find_tree(root : Node) -> Tree:
if root is Tree:
return root as Tree
for child in root.get_children():
var tree := __find_tree(child)
if tree:
return tree
return null
func __set_selected_node(node : NodePath, root : TreeItem):
if !root:
return false
for i in range(columns):
if root.get_metadata(i) == node:
GDBUIUtility.uncollapse_tree_item(root)
root.select(i)
return true
if __set_selected_node(node, root.get_children()):
return true
else:
return __set_selected_node(node, root.get_next())
func __full_path(rel_path : NodePath) -> NodePath:
assert(!rel_path.is_absolute())
var node := get_tree().edited_scene_root.get_node_or_null(rel_path)
if !node:
# printerr("EditorNodeTree: could not find node: %s" % rel_path)
return NodePath()
var res_path := node.get_path()
if rel_path.get_subname_count() > 0:
res_path = NodePath(str(res_path) + ":" + rel_path.get_concatenated_subnames())
return res_path
func __format_tree_item(tree : Tree, item : TreeItem) -> String:
var parts := PoolStringArray()
for i in range(tree.columns):
parts.append("%s[%s]" % [item.get_text(i), item.get_metadata(i)])
return parts.join("|")
func __dump_tree_item(tree : Tree, item : TreeItem, indent := 0):
if !item:
return
print(" ".repeat(indent), __format_tree_item(tree, item))
__dump_tree_item(tree, item.get_children(), indent + 2)
__dump_tree_item(tree, item.get_next(), indent)
func _set_selected_node(value : NodePath):
if value == selected_node:
return
if value.is_empty():
var selected_item := get_selected()
if selected_item:
selected_item.deselect(0)
return
if value.is_absolute():
printerr("EditorNodeTree: trying to set absolute NodePath, aborting.")
return
var full_path := __full_path(value)
if full_path.is_empty():
return
__set_selected_node(full_path, get_root())
selected_node = value
############
# handlers #
############
func _on_visiblity_changed():
if visible:
update()
func _on_item_selected():
var item := get_selected()
if !item:
selected_node = NodePath()
emit_signal("selected_node_changed")
return
for i in range(columns):
var meta = item.get_metadata(i)
if meta is NodePath:
assert(meta.is_absolute())
var node : Node = get_node(meta)
if !node:
printerr("EditorNodeTree contains invalid node path: %s" % meta)
return
selected_node = NodePath(str(get_tree().edited_scene_root.get_path_to(node)) + ":" + meta.get_concatenated_subnames())
emit_signal("selected_node_changed")
return
printerr("EditorNodeTree: selected node without NodePath?!")
###########
# signals #
###########
signal selected_node_changed()

View File

@ -0,0 +1,280 @@
extends Tree
class_name UIB_FileList
enum ViewMode {
LIST,
DETAILS
}
enum SpecialColumn {
CHECKBOX
}
enum Checkable {
NONE,
FILES,
FOLDERS,
BOTH
}
class DetailsColumn:
func _get_name() -> String:
return ""
func _get_value(entry) -> String:
return ""
func _get_expand() -> bool:
return false
func _get_min_width() -> float:
return 10.0
func _compare(entry0, entry1) -> bool:
return _get_value(entry0) < _get_value(entry1)
class DetailsColumnName:
extends DetailsColumn
func _get_name() -> String:
return tr("Name")
func _get_value(entry) -> String:
return entry._get_name()
func _get_expand() -> bool:
return true
class DetailsColumnType:
extends DetailsColumn
func _get_name() -> String:
return tr("Type")
func _get_value(entry) -> String:
if entry._is_folder():
return tr("Folder")
else:
return tr("File")
func _get_min_width() -> float:
return 100.0
class DetailsColumnSize:
extends DetailsColumn
func _get_name() -> String:
return tr("Size")
func _get_value(entry) -> String:
if entry._is_folder():
return ""
else:
return String.humanize_size(entry._get_size())
func _get_min_width() -> float:
return 100.0
func _compare(entry0, entry1) -> bool:
return entry0._get_size() < entry1._get_size()
class DetailsColumnModifiedTime:
extends DetailsColumn
func _get_name() -> String:
return tr("Modified")
func _get_value(entry) -> String:
if entry._is_folder():
return ""
else:
return GDBFormat.smart_format_unixtime(entry._get_modified_time())
func _get_min_width() -> float:
return 150.0
func _compare(entry0, entry1) -> bool:
return entry0._get_modified_time() < entry1._get_modified_time()
class __ParentFolderEntry:
extends UIB_FileAdapter.FileEntry
var real_entry
func _init(real_entry_):
real_entry = real_entry_
func _get_name() -> String:
return tr("<up>")
func _is_folder() -> bool:
return true
const CHECKBOX_COLUMN_WIDTH = 25
export(ViewMode) var view_mode = ViewMode.DETAILS setget _set_view_mode
export(Checkable) var checkable = Checkable.NONE setget _set_checkable
export var allow_navigation := true
export var show_up_entry := true
var adapter : UIB_FileAdapter setget _set_adapter
var filter : UIB_FileFilter setget _set_filter
var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder
var details_columns := [
DetailsColumnName.new(),
DetailsColumnType.new(),
DetailsColumnSize.new(),
DetailsColumnModifiedTime.new()
]
var __special_columns := []
var __sort_column := 0
var __sort_reverse := false
func _ready():
hide_root = true
select_mode = Tree.SELECT_ROW
if adapter == null:
adapter = UIB_LocalFileAdapter.new()
if current_folder == null:
current_folder = adapter._get_root()
__update_entries()
connect("item_activated", self, "_on_item_activated")
connect("item_selected", self, "_on_item_selected")
connect("column_title_pressed", self, "_on_column_title_pressed")
#################
# private stuff #
#################
func __compare_entries(entry0, entry1):
if entry0._is_folder() != entry1._is_folder():
return entry0._is_folder()
var val = details_columns[__sort_column]._compare(entry0, entry1)
if __sort_reverse:
return !val
return val
func __update_columns():
__special_columns.clear()
if checkable != Checkable.NONE:
__special_columns.append(SpecialColumn.CHECKBOX)
match view_mode:
_: # DETAILS
columns = __special_columns.size() + details_columns.size()
set_column_titles_visible(true)
for i in range(details_columns.size()):
set_column_title(__special_columns.size() + i, details_columns[i]._get_name())
set_column_expand(i, details_columns[i]._get_expand())
set_column_min_width(i, details_columns[i]._get_min_width())
for i in range(__special_columns.size()):
set_column_title(i, "")
set_column_expand(i, false)
set_column_min_width(i, CHECKBOX_COLUMN_WIDTH)
func __entry_is_checkable(entry):
match checkable:
Checkable.BOTH:
return true
Checkable.FILES:
return !entry._is_folder()
Checkable.FOLDERS:
return entry._is_folder()
_:
return false
func __update_entries():
if !current_folder:
return
clear()
create_item() # root
__update_columns()
if columns < 1:
return
var entries := current_folder._list_files()
entries.sort_custom(self, "__compare_entries")
if allow_navigation && show_up_entry:
var parent_folder = current_folder._get_parent()
if parent_folder:
entries.push_front(__ParentFolderEntry.new(parent_folder))
for entry in entries:
if filter && !entry._is_folder() && !filter._accepts(entry):
continue
var itm = create_item()
for i in range(__special_columns.size()):
match __special_columns[i]:
SpecialColumn.CHECKBOX:
if __entry_is_checkable(entry):
itm.set_cell_mode(i, TreeItem.CELL_MODE_CHECK)
for i in range(details_columns.size()):
itm.set_text(__special_columns.size() + i, details_columns[i]._get_value(entry))
itm.set_icon(__special_columns.size(), adapter._get_icon(entry))
itm.set_metadata(0, entry)
###########
# setters #
###########
func _set_view_mode(value):
if value != view_mode:
view_mode = value
__update_entries()
func _set_adapter(value : UIB_FileAdapter):
if value != adapter:
adapter = value
__update_entries()
func _set_filter(value : UIB_FileFilter):
if value != filter:
filter = value
__update_entries()
func _set_current_folder(value : UIB_FileAdapter.FileEntry):
if value != current_folder:
current_folder = value
emit_signal("current_folder_changed")
__update_entries()
func _set_checkable(value):
if value != checkable:
checkable = value
__update_entries()
############
# handlers #
############
func _on_item_activated():
var selected := get_selected()
if !selected:
return
var entry = selected.get_metadata(0)
if !entry:
return
if allow_navigation && entry._is_folder():
if entry is __ParentFolderEntry:
_set_current_folder(entry.real_entry)
else:
_set_current_folder(entry)
else:
emit_signal("file_activated", entry)
func _on_item_selected():
var selected := get_selected()
if !selected:
return
var entry = selected.get_metadata(0)
if !entry:
return
emit_signal("file_selected", entry)
func _on_column_title_pressed(idx : int):
var sort_idx := idx - __special_columns.size()
if sort_idx >= details_columns.size():
return
if sort_idx == __sort_column:
__sort_reverse = !__sort_reverse
else:
__sort_column = sort_idx
__sort_reverse = false
__update_entries()
###########
# signals #
###########
signal current_folder_changed()
signal file_activated(file_entry)
signal file_selected(file_entry)

View File

@ -0,0 +1,43 @@
tool
extends Container
const SORT_DELAY = 0.02
class_name UIB_FlowContainer
export var use_minimum_size := false
export var gap := 0.0
#############
# overrides #
#############
func _notification(what):
if what == NOTIFICATION_SORT_CHILDREN:
__sort_children()
#################
# private stuff #
#################
func __sort_children():
var pos := Vector2()
var row_height := 0.0
var vp_rect := get_viewport_rect()
for child in get_children():
if !child.visible || !child is Control:
continue
if use_minimum_size:
child.rect_size = child.get_combined_minimum_size()
if pos.x > 0.0 && pos.x + child.rect_size.x > rect_size.x:
pos.x = 0.0
pos.y += row_height + gap
row_height = 0.0
child.rect_position = pos
row_height = max(row_height, child.rect_size.y)
pos.x += child.rect_size.x + gap
rect_min_size.y = pos.y + row_height

View File

@ -0,0 +1,79 @@
extends LineEdit
class_name UIB_FolderEdit
var suggestion_popup := UIB_SuggestionPopup.new()
var adapter : UIB_FileAdapter
var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder
#############
# overrides #
#############
func _ready():
call_deferred("__setup")
connect("text_changed", self, "_on_text_changed")
connect("text_entered", self, "_on_text_entered")
#################
# private stuff #
#################
func __setup():
suggestion_popup.connect("suggestion_accepted", self, "_on_suggestion_accepted")
add_child(suggestion_popup)
func __set_suggestions(files : Array):
var suggestions := []
for file in files:
if file._is_folder():
suggestions.append(file._get_path() + "/")
suggestion_popup.suggestions = suggestions
func __update_suggestions():
var folder = adapter._get_file(text)
if folder && folder._is_folder():
__set_suggestions(folder._list_files())
return
var parent_path = text.get_base_dir()
if parent_path:
folder = adapter._get_file(parent_path)
if folder && folder._is_folder():
__set_suggestions(folder._list_files())
return
suggestion_popup.suggestions = []
func __update_current_folder():
var new_file = adapter._get_file(text)
if new_file:
_set_current_folder(new_file)
caret_position = text.length()
###########
# setters #
###########
func _set_current_folder(value):
if value != current_folder:
current_folder = value
text = current_folder._get_path()
emit_signal("current_folder_changed")
############
# handlers #
############
func _on_text_changed(new_text):
__update_suggestions()
func _on_text_entered(new_text):
__update_current_folder()
func _on_suggestion_accepted(suggestion):
__update_current_folder()
__update_suggestions()
###########
# signals #
###########
signal current_folder_changed()

View File

@ -0,0 +1,104 @@
extends Node
class_name UIB_FolderSync
enum DefaultFolder {
HOME,
ROOT,
CUSTOM
}
export(Array, NodePath) var nodes = []
export(DefaultFolder) var default_folder = DefaultFolder.HOME
export(NodePath) var folder_up_action
export(NodePath) var folder_favourite_action
export var custom_default_folder := ""
var adapter : UIB_FileAdapter = UIB_LocalFileAdapter.new()
var current_folder : UIB_FileAdapter.FileEntry setget set_current_folder
var __folder_up_action : UIB_SimpleAction
var __folder_favourite_action : UIB_SimpleAction
var __nodes := []
var __locked := false
#############
# overrides #
#############
func _ready():
__folder_up_action = __get_action(folder_up_action)
__folder_favourite_action = __get_action(folder_favourite_action)
if __folder_up_action:
__folder_up_action.connect("applied", self, "_on_folder_up_action")
if __folder_favourite_action:
__folder_favourite_action.connect("applied", self, "_on_folder_favourite_action")
var folder := "/"
match default_folder:
DefaultFolder.HOME:
folder = GDBFsUtil.get_home_folder()
DefaultFolder.CUSTOM:
folder = custom_default_folder
for path in nodes:
var node := get_node(path)
assert(node)
if !node:
continue
node.adapter = adapter
node.connect("current_folder_changed", self, "_on_node_current_folder_changed", [node])
__nodes.append(node)
var folder_entry = adapter._get_file(folder)
if folder_entry && folder_entry._is_folder():
set_current_folder(folder_entry)
################
# public stuff #
################
func set_current_folder(folder, ignore_node : Node = null):
if __locked:
return
__locked = true
for node in __nodes:
if node != ignore_node:
node.current_folder = folder
__locked = false
current_folder = folder
if __folder_up_action:
__folder_up_action.disabled = !folder || !folder._get_parent()
if __folder_favourite_action:
__folder_favourite_action.toggled = GDBUX.is_favourite_place(current_folder)
func __get_action(node_path : NodePath) -> UIB_SimpleAction:
var action_node = get_node_or_null(node_path)
if action_node is UIB_SimpleAction:
return action_node
elif action_node.has_method("get_action"):
return action_node.get_action() as UIB_SimpleAction
return null
############
# handlers #
############
func _on_node_current_folder_changed(source_node : Node):
set_current_folder(source_node.current_folder, source_node)
func _on_folder_up_action():
var parent = current_folder._get_parent()
if parent:
set_current_folder(parent)
func _on_folder_favourite_action():
if GDBUX.is_favourite_place(current_folder):
GDBUX.remove_favourite_place(current_folder)
__folder_favourite_action.toggled = false
else:
GDBUX.add_favourite_place(current_folder)
__folder_favourite_action.toggled = true

View File

@ -0,0 +1,16 @@
extends TabContainer
class_name UIB_NamedTabContainer
export(Array, String) var headers := []
export(Array, Texture) var icons := []
func _ready():
for i in range(headers.size()):
if i >= get_child_count():
break
set_tab_title(i, headers[i])
for i in range(icons.size()):
if i >= get_child_count():
break
set_tab_icon(i, icons[i])

View File

@ -0,0 +1,122 @@
extends ItemList
class_name UIB_PlacesList
enum Mode {
DEFAULT,
# FAVOURITES,
CUSTOM
}
class __DefaultItem:
var place : String
var icon : Texture
var label : String
func _init(place_ : String, icon_ : Texture, label_ : String):
place = place_
icon = icon_
label = label_
var __DEFAULT_ITEMS := [
__DefaultItem.new(GDBFsUtil.get_home_folder(), preload("res://addons/de.mewin.gduibasics/images/home32.svg"), tr("Home"))
]
export(Mode) var mode = Mode.DEFAULT setget _set_mode
export(Array, String) var places := [] setget _set_places
export(Array, Texture) var icons := []
export(Array, String) var names := []
var adapter : UIB_FileAdapter
var current_folder : UIB_FileAdapter.FileEntry setget _set_current_folder
#############
# overrides #
#############
func _ready():
call_deferred("__update_items")
connect("item_activated", self, "_on_item_activated")
GDBUX.connect_static("favourite_places_changed", self, "_on_favourite_places_changed")
#################
# private stuff #
#################
func __update_items():
clear()
if !adapter:
return
match mode:
Mode.DEFAULT:
__add_default_places()
_:
__add_custom_places()
func __add_default_places():
for default_place in __DEFAULT_ITEMS:
var folder := adapter._get_file(default_place.place)
if !folder || !folder._is_folder():
continue
__add_place(folder, default_place.icon, default_place.label)
for favourite in GDBUX.get_favourite_places():
var folder := adapter._get_file(favourite)
if !folder || !folder._is_folder():
continue
__add_place(folder, preload("res://addons/de.mewin.gduibasics/images/favourite32.svg"))
func __add_custom_places():
for i in range(places.size()):
var folder = adapter._get_file(places[i])
if !folder || !folder._is_folder():
continue
var label : String = names[i] if i < names.size() else ""
var icon : Texture = icons[i] if i < icons.size() else null
__add_place(folder, icon, label)
func __add_place(folder : UIB_FileAdapter.FileEntry, icon : Texture, label := ""):
if !icon:
icon = preload("res://addons/de.mewin.gduibasics/images/folder32.svg")
if !label:
label = folder._get_name()
var idx = get_item_count()
add_item(label, icon)
set_item_metadata(idx, folder)
func __select_current_folder():
for i in range(get_item_count()):
if get_item_metadata(i)._get_path() == current_folder._get_path():
select(i)
###########
# setters #
###########
func _set_mode(value):
if value != mode:
mode = value
__update_items()
func _set_places(value):
if value != places:
places = value
__update_items()
func _set_current_folder(value):
if value != current_folder:
current_folder = value
emit_signal("current_folder_changed")
############
# handlers #
############
func _on_item_activated(idx):
_set_current_folder(get_item_metadata(idx))
func _on_favourite_places_changed():
if mode == Mode.DEFAULT:
__update_items()
###########
# signals #
###########
signal current_folder_changed()

View File

@ -0,0 +1,18 @@
extends UIB_PlacesList
class_name UIB_RecentPlacesList
#############
# overrides #
#############
func _ready():
places = GDBSettings.get_value(GDBConstants.SETTING_RECENT_PLACES, [])
GDBSettings.connect_static("setting_changed", self, "_on_setting_changed")
############
# handlers #
############
func _on_setting_changed(setting, value):
if setting == GDBConstants.SETTING_RECENT_PLACES:
_set_places(value)

View File

@ -0,0 +1,58 @@
extends Control
class_name UIB_ScrollParallaxBackground
export(Array, Texture) var textures := []
export(Array, float) var speeds := []
export(NodePath) var scroll_container
var __scroll_container : ScrollContainer
func _ready():
show_behind_parent = true
rect_clip_content = true
if !scroll_container:
for child in get_children():
if child is ScrollContainer:
scroll_container = get_path_to(child)
break
__scroll_container = get_node_or_null(scroll_container) as ScrollContainer
set_process(false)
if __scroll_container:
# __scroll_container.connect("scroll_started", self, "_on_scroll_started")
# __scroll_container.connect("scroll_ended", self, "_on_scroll_ended")
__scroll_container.get_h_scrollbar().connect("value_changed", self, "_on_scroll_value_changed")
__scroll_container.get_v_scrollbar().connect("value_changed", self, "_on_scroll_value_changed")
func _draw():
if !__scroll_container || __scroll_container.get_child_count() < 1:
return
var first_child := __scroll_container.get_child(0) as Control
if !first_child:
return
for i in range(textures.size()):
var texture : Texture = textures[i]
var speed : float = speeds[i] if i < speeds.size() else 1.0
var rel_scroll := __scroll_container.scroll_vertical / max(first_child.rect_size.y - __scroll_container.rect_size.y, 1.0)
var tex_size := rect_size
var tex_pos := Vector2()
tex_size.y = tex_size.x * (texture.get_height() / texture.get_width())
tex_pos.y = speed * -rel_scroll * (tex_size.y - rect_size.y)
draw_texture_rect(texture, Rect2(tex_pos, tex_size), false)
func _process(delta):
update()
func _on_scroll_started():
set_process(true)
func _on_scroll_ended():
set_process(false)
func _on_scroll_value_changed(value : float):
update()

View File

@ -0,0 +1,11 @@
extends CheckBox
class_name UIB_SettingCheckBox
export var setting_name = ""
func _ready():
pressed = GDBSettings.get_value(setting_name, false)
func _pressed():
GDBSettings.set_value(setting_name, pressed)

View File

@ -0,0 +1,45 @@
extends OptionButton
class_name UIB_SettingOptionButton
export var setting_name := ""
export(Array) var values := []
export(Array, String) var labels := []
export(Array, Texture) var icons := []
#############
# overrides #
#############
func _ready():
__setup()
connect("item_selected", self, "_on_item_selected")
#################
# private stuff #
#################
func __setup():
clear()
for i in range(values.size()):
var value : String = values[i]
var label : String = labels[i] if i < labels.size() else str(value)
var icn : Texture = icons[i] if i < icons.size() else null
add_item(label)
set_item_icon(i, icn)
if !setting_name:
return
var current_value = GDBSettings.get_value(setting_name)
var current_idx = values.find(current_value)
if current_idx >= 0:
select(current_idx)
############
# handlers #
############
func _on_item_selected(idx : int):
if setting_name:
GDBSettings.set_value(setting_name, values[idx])

View File

@ -0,0 +1,231 @@
extends Popup
class_name UIB_SuggestionPopup
export(NodePath) var line_edit_path = ".."
export(int, 1, 20) var max_values = 5
export(Array, String) var suggestions = [] setget _set_suggestions
export(StyleBox) var style_normal = preload("res://addons/de.mewin.gduibasics/styles/suggestion_line_normal.stylebox")
export(StyleBox) var style_active = preload("res://addons/de.mewin.gduibasics/styles/suggestion_line_active.stylebox")
onready var line_edit : LineEdit = get_node(line_edit_path)
var __v_box_container := VBoxContainer.new()
var __panel := Panel.new()
var __labels := []
var __filtered_suggestions := []
var __filter_text := ""
var __active_label := -1
var __scroll := 0
var __up_direction := false
#############
# overrides #
#############
func _ready():
call_deferred("__initial_setup")
func _gui_input(event : InputEvent):
if !event.is_pressed() || !event is InputEventMouseButton:
return
if event.button_index == BUTTON_LEFT:
__accept_suggestion()
accept_event()
elif event.button_index == BUTTON_WHEEL_DOWN:
if !__up_direction:
__focus_down()
else:
__focus_up()
elif event.button_index == BUTTON_WHEEL_UP:
if !__up_direction:
__focus_up()
else:
__focus_down()
#################
# private stuff #
#################
func __initial_setup():
__panel.show_behind_parent = true
add_child(__panel)
add_child(__v_box_container)
for i in range(max_values):
var label := Label.new()
label.size_flags_horizontal |= Control.SIZE_EXPAND
label.mouse_filter = Control.MOUSE_FILTER_PASS
label.connect("mouse_entered", self, "_on_label_mouse_entered", [i])
__labels.append(label)
__v_box_container.add_child(label)
__setup()
func __setup():
if !line_edit:
return
line_edit.focus_neighbour_top = "."
line_edit.focus_neighbour_bottom = "."
line_edit.connect("focus_entered", self, "_on_line_edit_focus_entered")
line_edit.connect("focus_exited", self, "_on_line_edit_focus_exited")
line_edit.connect("text_changed", self, "_on_line_edit_text_changed")
line_edit.connect("gui_input", self, "_on_line_edit_gui_input")
func __calc_rect() -> Rect2:
var size := Vector2(line_edit.rect_size.x, __v_box_container.get_combined_minimum_size().y)
var lower_rect := Rect2(line_edit.rect_global_position \
+ Vector2(0.0, line_edit.rect_size.y), size)
if get_viewport_rect().encloses(lower_rect):
if __up_direction:
__up_direction = false
__update_labels(false)
return lower_rect
else:
var upper_rect := Rect2(line_edit.rect_global_position \
- Vector2(0.0, size.y), size)
if !__up_direction:
__up_direction = true
__update_labels(false)
return upper_rect
func __get_label(idx : int):
assert(idx < __labels.size())
if __up_direction:
return __labels[__labels.size() - 1 - idx]
return __labels[idx]
func __score_suggestion(suggestion : String) -> float:
if suggestion.to_lower().begins_with(__filter_text):
return 0.5 + 0.5 * suggestion.similarity(line_edit.text)
else:
return 0.5 * suggestion.similarity(line_edit.text)
func __compare_suggestions(sugg0 : String, sugg1 : String):
var score0 := __score_suggestion(sugg0)
var score1 := __score_suggestion(sugg1)
if score0 != score1:
return score0 > score1
else:
return sugg0 < sugg1
func __filter():
__filter_text = line_edit.text.to_lower() # .left(line_edit.caret_position).to_lower()
__filtered_suggestions = suggestions.duplicate()
__filtered_suggestions.sort_custom(self, "__compare_suggestions")
func __update_labels(update_size := true):
for i in range(__labels.size()):
var label : Label = __get_label(i)
if i >= __filtered_suggestions.size():
label.visible = false
else:
label.visible = true
label.text = __filtered_suggestions[(i + __scroll) % __filtered_suggestions.size()]
if i == __active_label:
label.add_stylebox_override("normal", style_active)
else:
label.add_stylebox_override("normal", style_normal)
if update_size:
var rect := __calc_rect()
rect_position = rect.position
rect_size = rect.size
__panel.rect_size = rect_size
__v_box_container.rect_size = rect_size
func __focus_down():
if __active_label == __labels.size() - 1:
__scroll = min(__scroll + 1, __filtered_suggestions.size() - __labels.size())
if __scroll < 0:
__scroll = 0
else:
__active_label += 1
__update_labels()
line_edit.caret_position = line_edit.text.length()
func __focus_up():
if __active_label < 0:
return
elif __active_label > 0:
__active_label -= 1
elif __scroll > 0:
__scroll -= 1
else:
__active_label = -1
__update_labels()
line_edit.caret_position = line_edit.text.length()
func __accept_suggestion():
if __active_label < 0:
return
var suggestion = __get_label(__active_label).text
line_edit.text = suggestion
line_edit.caret_position = suggestion.length()
__active_label = -1
__scroll = 0
__filter()
__update_labels()
emit_signal("suggestion_accepted", suggestion)
###########
# setters #
###########
func _set_suggestions(value : Array):
suggestions = value
if visible:
__filter()
__update_labels()
############
# handlers #
############
func _on_line_edit_focus_entered():
__filter()
__update_labels()
popup()
func _on_line_edit_focus_exited():
visible = false
func _on_line_edit_text_changed(new_text : String):
if !line_edit || !line_edit.has_focus():
return
__active_label = -1
__scroll = 0
__filter()
__update_labels()
if !visible:
popup()
func _on_line_edit_gui_input(input_event : InputEvent):
if !input_event.is_pressed():
return
if input_event.is_action_pressed("ui_accept"):
__accept_suggestion()
elif input_event.is_action("ui_down"):
if !__up_direction:
__focus_down()
else:
__focus_up()
elif input_event.is_action("ui_up"):
if !__up_direction:
__focus_up()
else:
__focus_down()
func _on_label_mouse_entered(index : int):
if __up_direction:
index = __labels.size() - index - 1
if __active_label != index:
__active_label = index
__update_labels()
###########
# signals #
###########
signal suggestion_accepted(suggestion)

View File

@ -0,0 +1,41 @@
extends ItemList
class_name UIB_TabsItemList
export(NodePath) var tab_container
#############
# overrides #
#############
func _ready():
call_deferred("__setup")
connect("item_selected", self, "_on_item_selected")
#################
# private stuff #
#################
func __get_tab_container() -> TabContainer:
return get_node_or_null(tab_container) as TabContainer
func __setup():
var tab_container_ := __get_tab_container()
clear()
if !tab_container_:
return
for i in range(tab_container_.get_child_count()):
var title := tab_container_.get_tab_title(i)
var icon := tab_container_.get_tab_icon(i)
add_item(title, icon)
select(tab_container_.current_tab)
############
# handlers #
############
func _on_item_selected(idx : int):
var tab_container_ := __get_tab_container()
if tab_container_:
tab_container_.current_tab = idx

View File

@ -0,0 +1,77 @@
extends ScrollContainer
class_name UIB_TouchScrollContainer
export(float) var drag_threshold := 20.0
var __dragging := false
var __drag_start := Vector2()
var __drag_start_scroll := Vector2()
var __momentum := Vector2()
func _ready():
set_process(false)
get_h_scrollbar().modulate = Color.transparent
get_v_scrollbar().modulate = Color.transparent
func _input(event):
if event is InputEventMouseMotion && (event.button_mask & BUTTON_MASK_LEFT) && \
(__dragging || get_global_rect().has_point(event.position)):
if !__dragging:
var dragged = event.position - __drag_start
if abs(dragged.x) > drag_threshold \
|| abs(dragged.y) > drag_threshold:
__dragging = true
else:
__momentum = event.relative
if scroll_horizontal_enabled:
scroll_horizontal = __drag_start_scroll.x + __drag_start.x - event.position.x
if scroll_vertical_enabled:
scroll_vertical = __drag_start_scroll.y + __drag_start.y - event.position.y
elif event is InputEventMouseButton && event.button_index == BUTTON_LEFT \
&& get_global_rect().has_point(event.position):
if !event.pressed:
if __dragging:
__dragging = false
set_process(true) # process momentum
else:
__emulate_click(event)
else:
__drag_start = event.position
__drag_start_scroll = Vector2(scroll_horizontal, scroll_vertical)
get_tree().set_input_as_handled()
func _process(delta : float):
__momentum *= 1.0 - 1.5 * delta
if abs(__momentum.x) < 0.01 && abs(__momentum.y) < 0.01:
set_process(false)
return
if scroll_horizontal_enabled:
scroll_horizontal -= __momentum.x * 50.0 * delta
if scroll_vertical_enabled:
scroll_vertical -= __momentum.y * 50.0 * delta
func __emulate_click(orig_event : InputEventMouseButton):
if get_child_count() < 1:
return
var child = get_child(0) as Control
if !child:
return
set_process_input(false)
var down_event : InputEventMouseButton = orig_event.duplicate()
down_event.position = orig_event.global_position
down_event.pressed = true
Input.parse_input_event(down_event)
yield(get_tree(), "idle_frame")
var up_event : InputEventMouseButton = orig_event.duplicate()
up_event.position = orig_event.global_position
up_event.pressed = false
Input.parse_input_event(up_event)
set_process_input(true)

View File

@ -0,0 +1,28 @@
extends UIB_BorderContainer
class_name UIB_WindowBorderContainer
export var minimum_size := Vector2(100.0, 100.0)
#############
# overrides #
#############
func _win_get_position() -> Vector2:
return OS.window_position
func _win_get_size() -> Vector2:
return OS.window_size
func _win_get_mouse_position() -> Vector2:
return get_viewport().get_mouse_position() + OS.window_position
func _win_get_minimum_size() -> Vector2:
return minimum_size
func _win_set_position(pos : Vector2):
if pos != OS.window_position:
OS.window_position = pos
OS.window_maximized = false
func _win_set_size(size : Vector2):
OS.window_size = size

View File

@ -0,0 +1,8 @@
extends Node
class_name UIB_ActionReference
export(NodePath) var action
func get_action() -> UIB_Action:
return get_node_or_null(action) as UIB_Action

View File

@ -0,0 +1,25 @@
tool
extends Node
class_name UIB_DynamicGridConstraints
export(int, 1, 100) var colspan := 1 setget _set_colspan
export(int, 1, 100) var rowspan := 1 setget _set_rowspan
func _ready():
__trigger_update()
func __trigger_update():
var container := get_node_or_null("../..") as Container # cyclic inclusion, cannot use UIB_DynamicGridContainer
if container != null:
container.notification(Container.NOTIFICATION_SORT_CHILDREN)
func _set_colspan(value : int):
if value != colspan:
colspan = value
__trigger_update()
func _set_rowspan(value : int):
if value != rowspan:
rowspan = value
__trigger_update()

View File

@ -0,0 +1,50 @@
extends Reference
class_name UIB_FileAdapter
class FileEntry:
func _is_folder() -> bool:
return false
func _is_link() -> bool:
return false
func _list_files() -> Array:
return []
func _get_name() -> String:
return ""
func _get_size() -> int:
return 0
func _get_modified_time() -> int:
return 0
func _get_parent() -> FileEntry:
return null
func _get_path() -> String:
var parent := _get_parent()
if parent:
return parent._get_path().plus_file(_get_name())
var name := _get_name()
if name == "":
return "/"
return name
func _get_root() -> FileEntry:
return null
func _get_file(path : String) -> FileEntry:
return null
func _get_icon(entry) -> Texture:
if entry._is_folder():
return preload("res://addons/de.mewin.gduibasics/images/folder.svg")
else:
return preload("res://addons/de.mewin.gduibasics/images/file.svg")
func _get_drives() -> Array:
return [""]

View File

@ -0,0 +1,20 @@
extends UIB_FileFilter
class_name UIB_FileExtensionFilter
var __extensions : PoolStringArray
func _init(extensions_ : Array):
for ext in extensions_:
if !ext.begins_with("."):
ext = "." + ext
__extensions.append(ext)
func _accepts(entry) -> bool:
for ext in __extensions:
if entry._get_name().ends_with(ext):
return true
return false
func _get_default_name() -> String:
return tr("%s-Files") % __extensions.join(", ")

View File

@ -0,0 +1,16 @@
extends Reference
class_name UIB_FileFilter
var name := ""
func _accepts(entry) -> bool:
return true
func _get_name() -> String:
if name:
return name
return _get_default_name()
func _get_default_name() -> String:
return ""

View File

@ -0,0 +1,110 @@
extends UIB_FileAdapter
class_name UIB_LocalFileAdapter
var __dir = Directory.new()
class LocalFileEntry:
extends UIB_FileAdapter.FileEntry
var path : String
var is_link : bool
var is_folder : bool
var size := -1
var modified_time := -1
func _init(path_ : String, is_link_ : bool, is_folder_ : bool):
path = path_
is_link = is_link_
is_folder = is_folder_
if path.ends_with("/") && path != "/":
path = path.left(path.length() - 1)
func _is_folder() -> bool:
return is_folder
func _is_link() -> bool:
return is_link
func _list_files() -> Array:
if !is_folder:
return []
var dir := Directory.new()
if dir.open(path + "/") != OK:
return []
if dir.list_dir_begin(true) != OK:
return []
var fname := dir.get_next()
var files := []
while fname:
var fpath := path.plus_file(fname)
var ffolder := dir.current_is_dir()
var flink := false
files.append(LocalFileEntry.new(fpath, flink, ffolder))
fname = dir.get_next()
return files
func _get_name() -> String:
return path.get_file()
func _get_size() -> int:
if size == -1:
size = __calc_size()
return size
func _get_modified_time() -> int:
if modified_time == -1:
modified_time = __calc_modified_time()
return modified_time
func _get_parent() -> UIB_FileAdapter.FileEntry:
var parent_path := path.get_base_dir()
if parent_path && parent_path != path:
return LocalFileEntry.new(parent_path, false, true)
return null
func __calc_size() -> int:
if is_folder:
return 0
var file := File.new()
if file.open(path, File.READ) != OK:
return 0
return file.get_len()
func __calc_modified_time() -> int:
if is_folder:
return 0
var file := File.new()
return file.get_modified_time(path)
func _get_file(path : String) -> FileEntry:
if OS.has_feature("Windows"): # has "feature"
path = path.replace("\\", "/")
if path.length() < 2 || path[1] != ":":
path = "c:/" + path.lstrip("/")
if path.length() == 2: #
path += "/"
elif !path.begins_with("/"):
path = "/" + path # only drive letter, dir_exists() wouldnt work
var dir := Directory.new()
if dir.dir_exists(path):
return LocalFileEntry.new(path, false, true)
elif dir.file_exists(path):
return LocalFileEntry.new(path, false, false)
else:
return null
func _get_root() -> UIB_FileAdapter.FileEntry:
return _get_file("/")
func _get_drives() -> Array:
var dir := Directory.new()
var drives := []
for i in range(dir.get_drive_count()):
drives.append(dir.get_drive(i))
return drives

View File

@ -0,0 +1,5 @@
extends Node
class_name UIB_Seperator
export(String) var text := ""

View File

@ -0,0 +1,102 @@
extends UIB_Action
class_name UIB_SimpleAction
export var text := "" setget _set_text
export(Texture) var icon setget _set_icon
export(Texture) var untoggled_icon setget _set_untoggled_icon
export(String) var shortcut_action setget _set_shortcut_action
export var disabled := false setget _set_disabled
export var toggleable := false setget _set_toggleable
export var toggled := false setget _set_toggled
#############
# overrides #
#############
func _get_text() -> String:
return text
func _get_icon() -> Texture:
if untoggled_icon && !toggled:
return untoggled_icon
return icon
func _get_shortcut() -> ShortCut:
if shortcut_action:
var action_list = InputMap.get_action_list(shortcut_action)
if !action_list:
return null
var shortcut := ShortCut.new()
shortcut.shortcut = action_list[0]
return shortcut
return null
func _is_disabled() -> bool:
return disabled
func _is_toggleable() -> bool:
return toggleable
func _is_toggled() -> bool:
return toggled
func _toggle(value : bool):
if toggleable:
_set_toggled(value)
func _apply():
emit_signal("applied")
func _update():
emit_signal("update", self)
###########
# setters #
###########
func _set_text(value : String):
if value != text:
text = value
emit_signal("text_changed")
func _set_icon(value : Texture):
if value != icon:
icon = value
emit_signal("icon_changed")
func _set_untoggled_icon(value : Texture):
if value != untoggled_icon:
untoggled_icon = value
emit_signal("icon_changed")
func _set_shortcut_action(value : String):
if value != shortcut_action:
shortcut_action = value
emit_signal("shortcut_changed")
func _set_disabled(value : bool):
if value != disabled:
disabled = value
emit_signal("disabled_changed")
func _set_toggled(value : bool):
if value != toggled:
toggled = value
emit_signal("toggled_changed")
if toggled:
_set_toggleable(true)
if untoggled_icon:
emit_signal("icon_changed")
func _set_toggleable(value : bool):
if value != toggleable:
toggleable = value
emit_signal("toggleable_changed")
if !toggleable:
_set_toggled(false)
###########
# signals #
###########
signal applied()
signal update(action)
signal toggleable_changed()

View File

@ -0,0 +1,71 @@
extends Node
class_name UIB_Action, "res://addons/de.mewin.gduibasics/images/action.svg"
export var continuous_update = false setget _set_continuous_update
#############
# overrides #
#############
func _ready():
set_process(continuous_update)
func _process(delta):
_update()
func _unhandled_input(event : InputEvent):
if self._is_disabled() || !event.is_pressed():
return
var shortcut := self._get_shortcut()
if !shortcut || !shortcut.is_shortcut(event):
return
self._apply()
################
# overridables #
################
func _get_text() -> String:
return ""
func _get_icon() -> Texture:
return null
func _get_shortcut() -> ShortCut:
return null
func _is_disabled() -> bool:
return false
func _is_toggleable() -> bool:
return false
func _is_toggled() -> bool:
return false
func _toggle(value : bool):
pass
func _apply():
_toggle(!_is_toggled())
func _update():
pass
###########
# setters #
###########
func _set_continuous_update(value):
if value != continuous_update:
continuous_update = value
set_process(continuous_update)
###########
# signals #
###########
signal text_changed()
signal icon_changed()
signal shortcut_changed()
signal disabled_changed()
signal toggled_changed()

Binary file not shown.

Binary file not shown.