input (text/checkbox), form, button
This commit is contained in:
1
Assets/Icons/checkbox.svg
Normal file
1
Assets/Icons/checkbox.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 134 134" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M129.026,35.376l0,62.54c0,17.225 -13.984,31.208 -31.208,31.208l-62.416,0c-17.224,0 -31.208,-13.983 -31.208,-31.208l-0,-62.54c-0,-17.224 13.984,-31.208 31.208,-31.208l62.416,0c17.224,0 31.208,13.984 31.208,31.208Z" style="fill:none;stroke:#000;stroke-width:8.33px;"/></svg>
|
||||||
|
After Width: | Height: | Size: 746 B |
37
Assets/Icons/checkbox.svg.import
Normal file
37
Assets/Icons/checkbox.svg.import
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dn4dxn8hkrd64"
|
||||||
|
path="res://.godot/imported/checkbox.svg-b131741b9ad567d1cf024db4c0f11166.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Assets/Icons/checkbox.svg"
|
||||||
|
dest_files=["res://.godot/imported/checkbox.svg-b131741b9ad567d1cf024db4c0f11166.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=2.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
1
Assets/Icons/checkbox_disabled.svg
Normal file
1
Assets/Icons/checkbox_disabled.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 134 134" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M129.026,35.376l0,62.54c0,17.225 -13.984,31.208 -31.208,31.208l-62.416,0c-17.224,0 -31.208,-13.983 -31.208,-31.208l-0,-62.54c-0,-17.224 13.984,-31.208 31.208,-31.208l62.416,0c17.224,0 31.208,13.984 31.208,31.208Z" style="fill:none;stroke:#707070;stroke-width:8.33px;"/></svg>
|
||||||
|
After Width: | Height: | Size: 749 B |
37
Assets/Icons/checkbox_disabled.svg.import
Normal file
37
Assets/Icons/checkbox_disabled.svg.import
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bap17ryrkcyey"
|
||||||
|
path="res://.godot/imported/checkbox_disabled.svg-d293c35e32232bb898ac1dc4c01f3d34.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Assets/Icons/checkbox_disabled.svg"
|
||||||
|
dest_files=["res://.godot/imported/checkbox_disabled.svg-d293c35e32232bb898ac1dc4c01f3d34.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
1
Assets/Icons/checkbox_pressed.svg
Normal file
1
Assets/Icons/checkbox_pressed.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 134 134" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M129.026,35.376l0,62.54c0,17.225 -13.984,31.208 -31.208,31.208l-62.416,0c-17.224,0 -31.208,-13.983 -31.208,-31.208l-0,-62.54c-0,-17.224 13.984,-31.208 31.208,-31.208l62.416,0c17.224,0 31.208,13.984 31.208,31.208Z" style="fill:#3f77ce;stroke:#3f77ce;stroke-width:8.33px;"/><path d="M34.151,60.318l-15.065,18.384l39.627,29.474l15.535,-18.591l-40.097,-29.267Z" style="fill:#fff;"/><path d="M114.247,41.46l-17.257,-16.344l-55.818,66.234l17.538,16.826l55.537,-66.716Z" style="fill:#fff;"/></svg>
|
||||||
|
After Width: | Height: | Size: 964 B |
37
Assets/Icons/checkbox_pressed.svg.import
Normal file
37
Assets/Icons/checkbox_pressed.svg.import
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://c5scqw224r2e5"
|
||||||
|
path="res://.godot/imported/checkbox_pressed.svg-7864c501a8fa0fa8194946d708755b56.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Assets/Icons/checkbox_pressed.svg"
|
||||||
|
dest_files=["res://.godot/imported/checkbox_pressed.svg-7864c501a8fa0fa8194946d708755b56.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
1
Assets/Icons/checkbox_pressed_grayscale.svg
Normal file
1
Assets/Icons/checkbox_pressed_grayscale.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 134 134" version="1.1" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M129.026,35.376l0,62.54c0,17.225 -13.984,31.208 -31.208,31.208l-62.416,0c-17.224,0 -31.208,-13.983 -31.208,-31.208l-0,-62.54c-0,-17.224 13.984,-31.208 31.208,-31.208l62.416,0c17.224,0 31.208,13.984 31.208,31.208Z" style="fill: #707070;stroke: #707070;stroke-width:8.33px;"/><path d="M34.151,60.318l-15.065,18.384l39.627,29.474l15.535,-18.591l-40.097,-29.267Z" style="fill: #ffffff;"/><path d="M114.247,41.46l-17.257,-16.344l-55.818,66.234l17.538,16.826l55.537,-66.716Z" style="fill: #ffffff;"/></svg>
|
||||||
|
After Width: | Height: | Size: 822 B |
37
Assets/Icons/checkbox_pressed_grayscale.svg.import
Normal file
37
Assets/Icons/checkbox_pressed_grayscale.svg.import
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b6ucuyluuw43"
|
||||||
|
path="res://.godot/imported/checkbox_pressed_grayscale.svg-45c89d5e4123fde397a9e7b9084c1a59.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Assets/Icons/checkbox_pressed_grayscale.svg"
|
||||||
|
dest_files=["res://.godot/imported/checkbox_pressed_grayscale.svg-45c89d5e4123fde397a9e7b9084c1a59.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
@@ -6,6 +6,7 @@ TODO:
|
|||||||
5. **Store** tab containers so switching tabs won't erase previous tab.
|
5. **Store** tab containers so switching tabs won't erase previous tab.
|
||||||
6. **GIF** support
|
6. **GIF** support
|
||||||
7. **Video** support via [GDE GoZen](https://github.com/VoylinsGamedevJourney/gde_gozen)
|
7. **Video** support via [GDE GoZen](https://github.com/VoylinsGamedevJourney/gde_gozen)
|
||||||
|
8. **More input types** (password, email, number, etc.)
|
||||||
|
|
||||||
Issues:
|
Issues:
|
||||||
1. **< br />** counts as 1 element in **WebsiteContainer**, therefore despite being (0,0) in size, it counts as double in spacing.
|
1. **< br />** counts as 1 element in **WebsiteContainer**, therefore despite being (0,0) in size, it counts as double in spacing.
|
||||||
@@ -1,4 +1,53 @@
|
|||||||
[gd_resource type="Theme" load_steps=7 format=3 uid="uid://bn6rbmdy60lhr"]
|
[gd_resource type="Theme" load_steps=17 format=3 uid="uid://bn6rbmdy60lhr"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dn4dxn8hkrd64" path="res://Assets/Icons/checkbox.svg" id="1_75mhk"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://b6ucuyluuw43" path="res://Assets/Icons/checkbox_pressed_grayscale.svg" id="2_2abar"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://c5scqw224r2e5" path="res://Assets/Icons/checkbox_pressed.svg" id="2_3k2jb"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bap17ryrkcyey" path="res://Assets/Icons/checkbox_disabled.svg" id="4_c32on"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_c32on"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7g0pl"]
|
||||||
|
bg_color = Color(0.168627, 0.168627, 0.168627, 1)
|
||||||
|
corner_radius_top_left = 4
|
||||||
|
corner_radius_top_right = 4
|
||||||
|
corner_radius_bottom_right = 4
|
||||||
|
corner_radius_bottom_left = 4
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c32on"]
|
||||||
|
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
|
||||||
|
corner_radius_top_left = 4
|
||||||
|
corner_radius_top_right = 4
|
||||||
|
corner_radius_bottom_right = 4
|
||||||
|
corner_radius_bottom_left = 4
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_75mhk"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jecr6"]
|
||||||
|
content_margin_left = 5.0
|
||||||
|
bg_color = Color(0.6, 0.6, 0.6, 0)
|
||||||
|
border_width_left = 3
|
||||||
|
border_width_top = 3
|
||||||
|
border_width_right = 3
|
||||||
|
border_width_bottom = 3
|
||||||
|
border_color = Color(0, 0, 0, 1)
|
||||||
|
corner_radius_top_left = 3
|
||||||
|
corner_radius_top_right = 3
|
||||||
|
corner_radius_bottom_right = 3
|
||||||
|
corner_radius_bottom_left = 3
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_75mhk"]
|
||||||
|
content_margin_left = 5.0
|
||||||
|
bg_color = Color(0.6, 0.6, 0.6, 0)
|
||||||
|
border_width_left = 1
|
||||||
|
border_width_top = 1
|
||||||
|
border_width_right = 1
|
||||||
|
border_width_bottom = 1
|
||||||
|
border_color = Color(0, 0, 0, 1)
|
||||||
|
corner_radius_top_left = 3
|
||||||
|
corner_radius_top_right = 3
|
||||||
|
corner_radius_bottom_right = 3
|
||||||
|
corner_radius_bottom_left = 3
|
||||||
|
|
||||||
[sub_resource type="SystemFont" id="SystemFont_jecr6"]
|
[sub_resource type="SystemFont" id="SystemFont_jecr6"]
|
||||||
font_names = PackedStringArray("Serif")
|
font_names = PackedStringArray("Serif")
|
||||||
@@ -23,6 +72,20 @@ font_names = PackedStringArray("Serif")
|
|||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_jecr6"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_jecr6"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
|
Button/styles/focus = SubResource("StyleBoxEmpty_c32on")
|
||||||
|
Button/styles/hover = SubResource("StyleBoxFlat_7g0pl")
|
||||||
|
Button/styles/normal = SubResource("StyleBoxFlat_c32on")
|
||||||
|
CheckBox/icons/checked = ExtResource("2_3k2jb")
|
||||||
|
CheckBox/icons/checked_disabled = ExtResource("2_2abar")
|
||||||
|
CheckBox/icons/unchecked = ExtResource("1_75mhk")
|
||||||
|
CheckBox/icons/unchecked_disabled = ExtResource("4_c32on")
|
||||||
|
CheckBox/styles/focus = SubResource("StyleBoxEmpty_75mhk")
|
||||||
|
LineEdit/colors/caret_color = Color(0, 0, 0, 1)
|
||||||
|
LineEdit/colors/font_color = Color(0, 0, 0, 1)
|
||||||
|
LineEdit/colors/font_placeholder_color = Color(0, 0, 0, 0.6)
|
||||||
|
LineEdit/styles/focus = SubResource("StyleBoxFlat_jecr6")
|
||||||
|
LineEdit/styles/normal = SubResource("StyleBoxFlat_75mhk")
|
||||||
|
LineEdit/styles/read_only = null
|
||||||
RichTextLabel/colors/font_selected_color = Color(1, 1, 1, 1)
|
RichTextLabel/colors/font_selected_color = Color(1, 1, 1, 1)
|
||||||
RichTextLabel/colors/selection_color = Color(0.313726, 0.403922, 0.8, 1)
|
RichTextLabel/colors/selection_color = Color(0.313726, 0.403922, 0.8, 1)
|
||||||
RichTextLabel/fonts/bold_font = SubResource("SystemFont_jecr6")
|
RichTextLabel/fonts/bold_font = SubResource("SystemFont_jecr6")
|
||||||
|
|||||||
20
Scenes/Tags/button.tscn
Normal file
20
Scenes/Tags/button.tscn
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://dhqqt4hg21w8d"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cks35eudcm1wj" path="res://Scripts/Tags/button.gd" id="1_button"]
|
||||||
|
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="2_theme"]
|
||||||
|
|
||||||
|
[node name="Button" type="Control"]
|
||||||
|
custom_minimum_size = Vector2(100, 30)
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 0
|
||||||
|
script = ExtResource("1_button")
|
||||||
|
|
||||||
|
[node name="ButtonNode" type="Button" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
offset_right = 100.0
|
||||||
|
offset_bottom = 30.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
mouse_default_cursor_shape = 2
|
||||||
|
theme = ExtResource("2_theme")
|
||||||
|
text = "Button"
|
||||||
10
Scenes/Tags/form.tscn
Normal file
10
Scenes/Tags/form.tscn
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bw8h5k6j2m3n4"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cn2iolk6biupv" path="res://Scripts/Tags/form.gd" id="1_form"]
|
||||||
|
|
||||||
|
[node name="Form" type="VBoxContainer"]
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
theme_override_constants/separation = 22
|
||||||
|
script = ExtResource("1_form")
|
||||||
34
Scenes/Tags/input.tscn
Normal file
34
Scenes/Tags/input.tscn
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://c7yay102a3b4c"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://kv6ebscarj2e" path="res://Scripts/Tags/input.gd" id="1_input"]
|
||||||
|
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="2_theme"]
|
||||||
|
|
||||||
|
[node name="Input" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 31.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 0
|
||||||
|
script = ExtResource("1_input")
|
||||||
|
|
||||||
|
[node name="LineEdit" type="LineEdit" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
offset_right = 400.0
|
||||||
|
offset_bottom = 35.0
|
||||||
|
theme = ExtResource("2_theme")
|
||||||
|
placeholder_text = "Enter text..."
|
||||||
|
caret_blink = true
|
||||||
|
|
||||||
|
[node name="CheckBox" type="CheckBox" parent="."]
|
||||||
|
visible = false
|
||||||
|
layout_mode = 0
|
||||||
|
offset_right = 31.0
|
||||||
|
offset_bottom = 31.0
|
||||||
|
theme = ExtResource("2_theme")
|
||||||
|
theme_override_constants/icon_max_width = 24
|
||||||
|
flat = true
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://bkj3x5y2m8qrl"]
|
[gd_scene load_steps=3 format=3 uid="uid://bkj3x5y2m8qrl"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cjk4x6y8m9wts" path="res://Scripts/Tags/span.gd" id="1_span"]
|
[ext_resource type="Script" uid="uid://4pbphta3r67k" path="res://Scripts/Tags/span.gd" id="1_span"]
|
||||||
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="2_theme"]
|
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="2_theme"]
|
||||||
|
|
||||||
[node name="SPAN" type="VBoxContainer"]
|
[node name="SPAN" type="VBoxContainer"]
|
||||||
@@ -17,5 +17,6 @@ mouse_default_cursor_shape = 1
|
|||||||
theme = ExtResource("2_theme")
|
theme = ExtResource("2_theme")
|
||||||
theme_override_colors/default_color = Color(0, 0, 0, 1)
|
theme_override_colors/default_color = Color(0, 0, 0, 1)
|
||||||
bbcode_enabled = true
|
bbcode_enabled = true
|
||||||
|
text = "Placeholder"
|
||||||
fit_content = true
|
fit_content = true
|
||||||
selection_enabled = true
|
selection_enabled = true
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=23 format=3 uid="uid://bytm7bt2s4ak8"]
|
[gd_scene load_steps=27 format=3 uid="uid://bytm7bt2s4ak8"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bg5iqnwic1rio" path="res://Scripts/main.gd" id="1_8q3xr"]
|
[ext_resource type="Script" uid="uid://bg5iqnwic1rio" path="res://Scripts/main.gd" id="1_8q3xr"]
|
||||||
[ext_resource type="Texture2D" uid="uid://df1m4j7uxi63v" path="res://Assets/Icons/chevron-down.svg" id="2_6bp64"]
|
[ext_resource type="Texture2D" uid="uid://df1m4j7uxi63v" path="res://Assets/Icons/chevron-down.svg" id="2_6bp64"]
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://sqhcxhcre081" path="res://Scenes/Tab.tscn" id="4_344ge"]
|
[ext_resource type="PackedScene" uid="uid://sqhcxhcre081" path="res://Scenes/Tab.tscn" id="4_344ge"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cu4hjoba6etf" path="res://Assets/Icons/rotate-cw.svg" id="5_344ge"]
|
[ext_resource type="Texture2D" uid="uid://cu4hjoba6etf" path="res://Assets/Icons/rotate-cw.svg" id="5_344ge"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cehbtwq6gq0cn" path="res://Assets/Icons/plus.svg" id="5_ynf5e"]
|
[ext_resource type="Texture2D" uid="uid://cehbtwq6gq0cn" path="res://Assets/Icons/plus.svg" id="5_ynf5e"]
|
||||||
|
[ext_resource type="Script" uid="uid://bgqglerkcylxx" path="res://addons/SmoothScroll/SmoothScrollContainer.gd" id="10_d1ilt"]
|
||||||
|
[ext_resource type="Script" uid="uid://b7h0k2h2qwlqv" path="res://addons/SmoothScroll/scroll_damper/expo_scroll_damper.gd" id="11_6iyac"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_344ge"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_344ge"]
|
||||||
|
|
||||||
@@ -61,6 +63,18 @@ expand_margin_left = 40.0
|
|||||||
LineEdit/styles/focus = SubResource("StyleBoxEmpty_8gbba")
|
LineEdit/styles/focus = SubResource("StyleBoxEmpty_8gbba")
|
||||||
LineEdit/styles/normal = SubResource("StyleBoxFlat_8gbba")
|
LineEdit/styles/normal = SubResource("StyleBoxFlat_8gbba")
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_fdnlq"]
|
||||||
|
script = ExtResource("11_6iyac")
|
||||||
|
friction = 4.0
|
||||||
|
minimum_velocity = 0.4
|
||||||
|
rebound_strength = 7.0
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_jkdf5"]
|
||||||
|
script = ExtResource("11_6iyac")
|
||||||
|
friction = 4.0
|
||||||
|
minimum_velocity = 0.4
|
||||||
|
rebound_strength = 7.0
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_white"]
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_white"]
|
||||||
bg_color = Color(1, 1, 1, 1)
|
bg_color = Color(1, 1, 1, 1)
|
||||||
|
|
||||||
@@ -190,14 +204,22 @@ stretch_mode = 5
|
|||||||
custom_minimum_size = Vector2(0, 5)
|
custom_minimum_size = Vector2(0, 5)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="WebsiteContainer" type="VBoxContainer" parent="VBoxContainer"]
|
[node name="SmoothScrollContainer" type="ScrollContainer" parent="VBoxContainer"]
|
||||||
unique_name_in_owner = true
|
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
theme_override_constants/separation = 22
|
script = ExtResource("10_d1ilt")
|
||||||
|
wheel_scroll_damper = SubResource("Resource_fdnlq")
|
||||||
|
dragging_scroll_damper = SubResource("Resource_jkdf5")
|
||||||
|
drag_with_mouse = false
|
||||||
|
allow_overdragging = false
|
||||||
|
metadata/_custom_type_script = "uid://bgqglerkcylxx"
|
||||||
|
|
||||||
[node name="Control" type="Control" parent="VBoxContainer/WebsiteContainer"]
|
[node name="WebsiteContainer" type="VBoxContainer" parent="VBoxContainer/SmoothScrollContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 3
|
||||||
|
theme_override_constants/separation = 22
|
||||||
|
|
||||||
[node name="WebsiteBackground" type="Panel" parent="."]
|
[node name="WebsiteBackground" type="Panel" parent="."]
|
||||||
z_index = -1
|
z_index = -1
|
||||||
@@ -213,18 +235,24 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_white")
|
|||||||
|
|
||||||
[node name="Panel" type="Panel" parent="."]
|
[node name="Panel" type="Panel" parent="."]
|
||||||
z_index = -5
|
z_index = -5
|
||||||
layout_mode = 2
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
offset_top = 58.0
|
offset_top = 58.0
|
||||||
offset_right = 1920.0
|
grow_horizontal = 2
|
||||||
offset_bottom = 125.0
|
grow_vertical = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_6iyac")
|
theme_override_styles/panel = SubResource("StyleBoxFlat_6iyac")
|
||||||
|
|
||||||
[node name="Panel2" type="Panel" parent="."]
|
[node name="Panel2" type="Panel" parent="."]
|
||||||
z_index = -6
|
z_index = -6
|
||||||
layout_mode = 2
|
layout_mode = 1
|
||||||
offset_right = 1920.0
|
anchors_preset = 15
|
||||||
offset_bottom = 125.0
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
mouse_filter = 2
|
mouse_filter = 2
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_21xkr")
|
theme_override_styles/panel = SubResource("StyleBoxFlat_21xkr")
|
||||||
|
|
||||||
|
|||||||
@@ -80,9 +80,6 @@ class HTMLElement:
|
|||||||
|
|
||||||
func is_inline_element() -> bool:
|
func is_inline_element() -> bool:
|
||||||
return tag_name in ["b", "i", "u", "small", "mark", "code", "span", "a"]
|
return tag_name in ["b", "i", "u", "small", "mark", "code", "span", "a"]
|
||||||
|
|
||||||
func is_heading_element() -> bool:
|
|
||||||
return tag_name in ["h1", "h2", "h3", "h4", "h5", "h6"]
|
|
||||||
|
|
||||||
class ParseResult:
|
class ParseResult:
|
||||||
var root: HTMLElement
|
var root: HTMLElement
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func create_tab() -> void:
|
|||||||
# WARNING: temporary
|
# WARNING: temporary
|
||||||
main.render()
|
main.render()
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(_event: InputEvent) -> void:
|
||||||
if Input.is_action_just_pressed("NewTab"):
|
if Input.is_action_just_pressed("NewTab"):
|
||||||
create_tab()
|
create_tab()
|
||||||
if Input.is_action_just_pressed("CloseTab"):
|
if Input.is_action_just_pressed("CloseTab"):
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
func init(element: HTMLParser.HTMLElement) -> void:
|
func init(_element: HTMLParser.HTMLElement) -> void:
|
||||||
pass
|
pass
|
||||||
|
|||||||
18
Scripts/Tags/button.gd
Normal file
18
Scripts/Tags/button.gd
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
extends Control
|
||||||
|
|
||||||
|
func init(element: HTMLParser.HTMLElement) -> void:
|
||||||
|
var button_node: Button = $ButtonNode
|
||||||
|
|
||||||
|
var button_text = element.text_content.strip_edges()
|
||||||
|
if button_text.length() == 0:
|
||||||
|
button_text = element.get_bbcode_formatted_text()
|
||||||
|
|
||||||
|
if button_text.length() > 0:
|
||||||
|
button_node.text = button_text
|
||||||
|
|
||||||
|
button_node.custom_minimum_size = button_node.get_theme_default_font().get_string_size(
|
||||||
|
button_node.text,
|
||||||
|
HORIZONTAL_ALIGNMENT_LEFT,
|
||||||
|
-1,
|
||||||
|
button_node.get_theme_default_font_size()
|
||||||
|
) + Vector2(20, 10) # Add padding
|
||||||
1
Scripts/Tags/button.gd.uid
Normal file
1
Scripts/Tags/button.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cks35eudcm1wj
|
||||||
4
Scripts/Tags/form.gd
Normal file
4
Scripts/Tags/form.gd
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
extends VBoxContainer
|
||||||
|
|
||||||
|
func init(_element: HTMLParser.HTMLElement) -> void:
|
||||||
|
pass
|
||||||
1
Scripts/Tags/form.gd.uid
Normal file
1
Scripts/Tags/form.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cn2iolk6biupv
|
||||||
23
Scripts/Tags/input.gd
Normal file
23
Scripts/Tags/input.gd
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
extends Control
|
||||||
|
|
||||||
|
func init(element: HTMLParser.HTMLElement) -> void:
|
||||||
|
var line_edit: LineEdit = $LineEdit
|
||||||
|
var check_box: CheckBox = $CheckBox
|
||||||
|
|
||||||
|
var input_type = element.get_attribute("type").to_lower()
|
||||||
|
var placeholder = element.get_attribute("placeholder")
|
||||||
|
var value = element.get_attribute("value")
|
||||||
|
|
||||||
|
match input_type:
|
||||||
|
"checkbox":
|
||||||
|
line_edit.visible = false
|
||||||
|
check_box.visible = true
|
||||||
|
if value and value == "true": check_box.button_pressed = true
|
||||||
|
custom_minimum_size = check_box.size
|
||||||
|
_: # Default to text input
|
||||||
|
line_edit.visible = true
|
||||||
|
check_box.visible = false
|
||||||
|
custom_minimum_size = line_edit.size
|
||||||
|
|
||||||
|
if placeholder: line_edit.placeholder_text = placeholder
|
||||||
|
if value: line_edit.text = value
|
||||||
1
Scripts/Tags/input.gd.uid
Normal file
1
Scripts/Tags/input.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://kv6ebscarj2e
|
||||||
@@ -19,6 +19,16 @@ const H3 = preload("res://Scenes/Tags/h3.tscn")
|
|||||||
const H4 = preload("res://Scenes/Tags/h4.tscn")
|
const H4 = preload("res://Scenes/Tags/h4.tscn")
|
||||||
const H5 = preload("res://Scenes/Tags/h5.tscn")
|
const H5 = preload("res://Scenes/Tags/h5.tscn")
|
||||||
const H6 = preload("res://Scenes/Tags/h6.tscn")
|
const H6 = preload("res://Scenes/Tags/h6.tscn")
|
||||||
|
const FORM = preload("res://Scenes/Tags/form.tscn")
|
||||||
|
const INPUT = preload("res://Scenes/Tags/input.tscn")
|
||||||
|
const BUTTON = preload("res://Scenes/Tags/button.tscn")
|
||||||
|
|
||||||
|
const MIN_SIZE = Vector2i(750, 200)
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
ProjectSettings.set_setting("display/window/size/min_width", MIN_SIZE.x)
|
||||||
|
ProjectSettings.set_setting("display/window/size/min_height", MIN_SIZE.y)
|
||||||
|
DisplayServer.window_set_min_size(MIN_SIZE)
|
||||||
|
|
||||||
func render():
|
func render():
|
||||||
# Clear existing content
|
# Clear existing content
|
||||||
@@ -64,6 +74,17 @@ both spaces and
|
|||||||
line breaks
|
line breaks
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<span>Name:</span>
|
||||||
|
<input type=\"text\" placeholder=\"First name\" value=\"John\" />
|
||||||
|
<span>Surname:</span>
|
||||||
|
<input type=\"text\" placeholder=\"Last name\" value=\"Doe\" />
|
||||||
|
<span>Smart:</span>
|
||||||
|
<input type=\"checkbox\" />
|
||||||
|
<input type=\"checkbox\" value=\"true\" />
|
||||||
|
<button>Submit</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
<separator direction=\"horizontal\" />
|
<separator direction=\"horizontal\" />
|
||||||
<img src=\"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQMNUPIKabszX0Js_c0kfa4cz_JQYKfGTuBUA&s\" />
|
<img src=\"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQMNUPIKabszX0Js_c0kfa4cz_JQYKfGTuBUA&s\" />
|
||||||
<separator direction=\"vertical\" />
|
<separator direction=\"vertical\" />
|
||||||
@@ -176,6 +197,24 @@ line breaks
|
|||||||
var separator = SEPARATOR.instantiate()
|
var separator = SEPARATOR.instantiate()
|
||||||
separator.init(element)
|
separator.init(element)
|
||||||
website_container.add_child(separator)
|
website_container.add_child(separator)
|
||||||
|
"form":
|
||||||
|
var form = FORM.instantiate()
|
||||||
|
form.init(element)
|
||||||
|
website_container.add_child(form)
|
||||||
|
|
||||||
|
# Render form children
|
||||||
|
for child_element in element.children:
|
||||||
|
var child_node = create_element_node(child_element)
|
||||||
|
if child_node:
|
||||||
|
form.add_child(child_node)
|
||||||
|
"input":
|
||||||
|
var input = INPUT.instantiate()
|
||||||
|
input.init(element)
|
||||||
|
website_container.add_child(input)
|
||||||
|
"button":
|
||||||
|
var button = BUTTON.instantiate()
|
||||||
|
button.init(element)
|
||||||
|
website_container.add_child(button)
|
||||||
"span":
|
"span":
|
||||||
var span = SPAN.instantiate()
|
var span = SPAN.instantiate()
|
||||||
span.init(element)
|
span.init(element)
|
||||||
@@ -213,3 +252,20 @@ func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool:
|
|||||||
return true
|
return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
func create_element_node(element: HTMLParser.HTMLElement) -> Control:
|
||||||
|
match element.tag_name:
|
||||||
|
"input":
|
||||||
|
var input = INPUT.instantiate()
|
||||||
|
input.init(element)
|
||||||
|
return input
|
||||||
|
"button":
|
||||||
|
var button = BUTTON.instantiate()
|
||||||
|
button.init(element)
|
||||||
|
return button
|
||||||
|
"span":
|
||||||
|
var span = SPAN.instantiate()
|
||||||
|
span.init(element)
|
||||||
|
return span
|
||||||
|
_:
|
||||||
|
return null
|
||||||
|
|||||||
916
addons/SmoothScroll/SmoothScrollContainer.gd
Normal file
916
addons/SmoothScroll/SmoothScrollContainer.gd
Normal file
@@ -0,0 +1,916 @@
|
|||||||
|
## Smooth scroll functionality for ScrollContainer
|
||||||
|
##
|
||||||
|
## Applies velocity based momentum and "overdrag"
|
||||||
|
## functionality to a ScrollContainer
|
||||||
|
@tool
|
||||||
|
extends ScrollContainer
|
||||||
|
class_name SmoothScrollContainer
|
||||||
|
|
||||||
|
@export_group("Mouse Wheel")
|
||||||
|
## Drag impact for one scroll input
|
||||||
|
@export_range(0, 10, 0.01, "or_greater", "hide_slider")
|
||||||
|
var speed := 1000.0
|
||||||
|
## ScrollDamper for wheel scrolling
|
||||||
|
@export
|
||||||
|
var wheel_scroll_damper: ScrollDamper = ExpoScrollDamper.new()
|
||||||
|
|
||||||
|
@export_group("Dragging")
|
||||||
|
## ScrollDamper for dragging
|
||||||
|
@export
|
||||||
|
var dragging_scroll_damper: ScrollDamper = ExpoScrollDamper.new()
|
||||||
|
### Allow dragging with mouse or not
|
||||||
|
@export
|
||||||
|
var drag_with_mouse := true
|
||||||
|
## Allow dragging with touch or not
|
||||||
|
@export
|
||||||
|
var drag_with_touch := true
|
||||||
|
|
||||||
|
@export_group("Container")
|
||||||
|
## Below this value, snap content to boundary
|
||||||
|
@export
|
||||||
|
var just_snap_under := 0.4
|
||||||
|
## Margin of the currently focused element
|
||||||
|
@export_range(0, 50)
|
||||||
|
var follow_focus_margin := 20
|
||||||
|
## Makes the container scrollable vertically
|
||||||
|
@export
|
||||||
|
var allow_vertical_scroll := true
|
||||||
|
## Makes the container scrollable horizontally
|
||||||
|
@export
|
||||||
|
var allow_horizontal_scroll := true
|
||||||
|
## Makes the container only scrollable where the content has overflow
|
||||||
|
@export
|
||||||
|
var auto_allow_scroll := true
|
||||||
|
## Whether the content of this container should be allowed to overshoot at the ends
|
||||||
|
## before interpolating back to its bounds
|
||||||
|
@export
|
||||||
|
var allow_overdragging := true
|
||||||
|
|
||||||
|
@export_group("Scroll Bar")
|
||||||
|
## Hides scrollbar as long as not hovered or interacted with
|
||||||
|
@export
|
||||||
|
var hide_scrollbar_over_time := false:
|
||||||
|
set(val): hide_scrollbar_over_time = _set_hide_scrollbar_over_time(val)
|
||||||
|
## Time after scrollbar starts to fade out when 'hide_scrollbar_over_time' is true
|
||||||
|
@export
|
||||||
|
var scrollbar_hide_time := 5.0
|
||||||
|
## Fadein time for scrollbar when 'hide_scrollbar_over_time' is true
|
||||||
|
@export
|
||||||
|
var scrollbar_fade_in_time := 0.2
|
||||||
|
## Fadeout time for scrollbar when 'hide_scrollbar_over_time' is true
|
||||||
|
@export
|
||||||
|
var scrollbar_fade_out_time := 0.5
|
||||||
|
|
||||||
|
@export_group("Input")
|
||||||
|
## If true sets the input event as handled with set_input_as_handled()
|
||||||
|
@export
|
||||||
|
var handle_input := true
|
||||||
|
|
||||||
|
@export_group("Debug")
|
||||||
|
## Adds debug information
|
||||||
|
@export
|
||||||
|
var debug_mode := false
|
||||||
|
|
||||||
|
## Current velocity of the `content_node`
|
||||||
|
var velocity := Vector2(0,0)
|
||||||
|
## Control node to move when scrolling
|
||||||
|
var content_node: Control
|
||||||
|
## Current position of `content_node`
|
||||||
|
var pos := Vector2(0, 0)
|
||||||
|
## Current ScrollDamper to use, recording to last input type
|
||||||
|
var scroll_damper: ScrollDamper
|
||||||
|
## When true, `content_node`'s position is only set by dragging the h scroll bar
|
||||||
|
var h_scrollbar_dragging := false
|
||||||
|
## When true, `content_node`'s position is only set by dragging the v scroll bar
|
||||||
|
var v_scrollbar_dragging := false
|
||||||
|
## When ture, `content_node` follows drag position
|
||||||
|
var content_dragging := false
|
||||||
|
## When ture, `content_node` has moved by dragging
|
||||||
|
var content_dragging_moved := false
|
||||||
|
## Timer for hiding scroll bar
|
||||||
|
var scrollbar_hide_timer := Timer.new()
|
||||||
|
## Tween for showing scroll bar
|
||||||
|
var scrollbar_show_tween: Tween
|
||||||
|
## Tween for hiding scroll bar
|
||||||
|
var scrollbar_hide_tween: Tween
|
||||||
|
## Tween for scroll x to
|
||||||
|
var scroll_x_to_tween: Tween
|
||||||
|
## Tween for scroll y to
|
||||||
|
var scroll_y_to_tween: Tween
|
||||||
|
## [0,1] Mouse or touch's relative movement accumulation when overdrag[br]
|
||||||
|
## [2,3] Position where dragging starts[br]
|
||||||
|
## [4,5,6,7] Left_distance, right_distance, top_distance, bottom_distance
|
||||||
|
var drag_temp_data := []
|
||||||
|
## Whether touch point is in deadzone.
|
||||||
|
var is_in_deadzone := false
|
||||||
|
## Whether mouse is on h or v scroll bar
|
||||||
|
var mouse_on_scrollbar := false
|
||||||
|
|
||||||
|
## If content is being scrolled
|
||||||
|
var is_scrolling := false:
|
||||||
|
set(val):
|
||||||
|
if is_scrolling != val:
|
||||||
|
if val:
|
||||||
|
emit_signal("scroll_started")
|
||||||
|
else:
|
||||||
|
emit_signal("scroll_ended")
|
||||||
|
is_scrolling = val
|
||||||
|
|
||||||
|
## Last type of input used to scroll
|
||||||
|
enum SCROLL_TYPE {WHEEL, BAR, DRAG}
|
||||||
|
var last_scroll_type: SCROLL_TYPE
|
||||||
|
|
||||||
|
#region Virtual Functions
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if debug_mode:
|
||||||
|
setup_debug_drawing()
|
||||||
|
# Initialize variables
|
||||||
|
scroll_damper = wheel_scroll_damper
|
||||||
|
|
||||||
|
get_v_scroll_bar().gui_input.connect(_scrollbar_input.bind(true))
|
||||||
|
get_h_scroll_bar().gui_input.connect(_scrollbar_input.bind(false))
|
||||||
|
get_v_scroll_bar().mouse_entered.connect(_mouse_on_scroll_bar.bind(true))
|
||||||
|
get_v_scroll_bar().mouse_exited.connect(_mouse_on_scroll_bar.bind(false))
|
||||||
|
get_h_scroll_bar().mouse_entered.connect(_mouse_on_scroll_bar.bind(true))
|
||||||
|
get_h_scroll_bar().mouse_exited.connect(_mouse_on_scroll_bar.bind(false))
|
||||||
|
get_viewport().gui_focus_changed.connect(_on_focus_changed)
|
||||||
|
|
||||||
|
for c in get_children():
|
||||||
|
if not c is ScrollBar:
|
||||||
|
content_node = c
|
||||||
|
|
||||||
|
add_child(scrollbar_hide_timer)
|
||||||
|
scrollbar_hide_timer.one_shot = true
|
||||||
|
scrollbar_hide_timer.timeout.connect(_scrollbar_hide_timer_timeout)
|
||||||
|
if hide_scrollbar_over_time:
|
||||||
|
scrollbar_hide_timer.start(scrollbar_hide_time)
|
||||||
|
get_tree().node_added.connect(_on_node_added)
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
if Engine.is_editor_hint(): return
|
||||||
|
scroll(true, velocity.y, pos.y, delta)
|
||||||
|
scroll(false, velocity.x, pos.x, delta)
|
||||||
|
update_scrollbars()
|
||||||
|
update_is_scrolling()
|
||||||
|
|
||||||
|
if debug_mode:
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
# Detecting mouse entering and exiting scroll bar
|
||||||
|
func _mouse_on_scroll_bar(entered: bool) -> void:
|
||||||
|
mouse_on_scrollbar = entered
|
||||||
|
|
||||||
|
# Forwarding scroll inputs from scrollbar
|
||||||
|
func _scrollbar_input(event: InputEvent, vertical: bool) -> void:
|
||||||
|
if event is InputEventMouseButton:
|
||||||
|
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN\
|
||||||
|
or event.button_index == MOUSE_BUTTON_WHEEL_UP\
|
||||||
|
or event.button_index == MOUSE_BUTTON_WHEEL_LEFT\
|
||||||
|
or event.button_index == MOUSE_BUTTON_WHEEL_RIGHT:
|
||||||
|
_gui_input(event)
|
||||||
|
|
||||||
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
|
if event.pressed:
|
||||||
|
if vertical:
|
||||||
|
v_scrollbar_dragging = true
|
||||||
|
last_scroll_type = SCROLL_TYPE.BAR
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
else:
|
||||||
|
h_scrollbar_dragging = true
|
||||||
|
last_scroll_type = SCROLL_TYPE.BAR
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
else:
|
||||||
|
if vertical:
|
||||||
|
v_scrollbar_dragging = false
|
||||||
|
else:
|
||||||
|
h_scrollbar_dragging = false
|
||||||
|
|
||||||
|
if event is InputEventScreenTouch:
|
||||||
|
if event.pressed:
|
||||||
|
if vertical:
|
||||||
|
v_scrollbar_dragging = true
|
||||||
|
last_scroll_type = SCROLL_TYPE.BAR
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
else:
|
||||||
|
h_scrollbar_dragging = true
|
||||||
|
last_scroll_type = SCROLL_TYPE.BAR
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
else:
|
||||||
|
if vertical:
|
||||||
|
v_scrollbar_dragging = false
|
||||||
|
else:
|
||||||
|
h_scrollbar_dragging = false
|
||||||
|
|
||||||
|
func _gui_input(event: InputEvent) -> void:
|
||||||
|
# Show scroll bars when mouse moves
|
||||||
|
if hide_scrollbar_over_time and event is InputEventMouseMotion:
|
||||||
|
show_scrollbars()
|
||||||
|
|
||||||
|
if event is InputEventMouseButton:
|
||||||
|
match event.button_index:
|
||||||
|
MOUSE_BUTTON_WHEEL_DOWN:
|
||||||
|
if event.pressed:
|
||||||
|
last_scroll_type = SCROLL_TYPE.WHEEL
|
||||||
|
if event.shift_pressed or not should_scroll_vertical():
|
||||||
|
if should_scroll_horizontal():
|
||||||
|
velocity.x -= speed * event.factor
|
||||||
|
else:
|
||||||
|
if should_scroll_vertical():
|
||||||
|
velocity.y -= speed * event.factor
|
||||||
|
scroll_damper = wheel_scroll_damper
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
MOUSE_BUTTON_WHEEL_UP:
|
||||||
|
if event.pressed:
|
||||||
|
last_scroll_type = SCROLL_TYPE.WHEEL
|
||||||
|
if event.shift_pressed or not should_scroll_vertical():
|
||||||
|
if should_scroll_horizontal():
|
||||||
|
velocity.x += speed * event.factor
|
||||||
|
else:
|
||||||
|
if should_scroll_vertical():
|
||||||
|
velocity.y += speed * event.factor
|
||||||
|
scroll_damper = wheel_scroll_damper
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
MOUSE_BUTTON_WHEEL_LEFT:
|
||||||
|
if event.pressed:
|
||||||
|
last_scroll_type = SCROLL_TYPE.WHEEL
|
||||||
|
if event.shift_pressed:
|
||||||
|
if should_scroll_vertical():
|
||||||
|
velocity.y -= speed * event.factor
|
||||||
|
else:
|
||||||
|
if should_scroll_horizontal():
|
||||||
|
velocity.x += speed * event.factor
|
||||||
|
scroll_damper = wheel_scroll_damper
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
MOUSE_BUTTON_WHEEL_RIGHT:
|
||||||
|
if event.pressed:
|
||||||
|
last_scroll_type = SCROLL_TYPE.WHEEL
|
||||||
|
if event.shift_pressed:
|
||||||
|
if should_scroll_vertical():
|
||||||
|
velocity.y += speed * event.factor
|
||||||
|
else:
|
||||||
|
if should_scroll_horizontal():
|
||||||
|
velocity.x -= speed * event.factor
|
||||||
|
scroll_damper = wheel_scroll_damper
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
MOUSE_BUTTON_LEFT:
|
||||||
|
if event.pressed:
|
||||||
|
if !drag_with_mouse: return
|
||||||
|
content_dragging = true
|
||||||
|
is_in_deadzone = true
|
||||||
|
scroll_damper = dragging_scroll_damper
|
||||||
|
last_scroll_type = SCROLL_TYPE.DRAG
|
||||||
|
init_drag_temp_data()
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
else:
|
||||||
|
content_dragging = false
|
||||||
|
is_in_deadzone = false
|
||||||
|
|
||||||
|
if (event is InputEventScreenDrag and drag_with_touch) \
|
||||||
|
or (event is InputEventMouseMotion and drag_with_mouse):
|
||||||
|
if content_dragging:
|
||||||
|
if should_scroll_horizontal():
|
||||||
|
drag_temp_data[0] += event.relative.x
|
||||||
|
if should_scroll_vertical():
|
||||||
|
drag_temp_data[1] += event.relative.y
|
||||||
|
remove_all_children_focus(self)
|
||||||
|
handle_content_dragging()
|
||||||
|
|
||||||
|
if event is InputEventPanGesture:
|
||||||
|
if should_scroll_horizontal():
|
||||||
|
velocity.x = -event.delta.x * speed
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
if should_scroll_vertical():
|
||||||
|
velocity.y = -event.delta.y * speed
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
|
||||||
|
if event is InputEventScreenTouch:
|
||||||
|
if event.pressed:
|
||||||
|
if !drag_with_touch: return
|
||||||
|
content_dragging = true
|
||||||
|
is_in_deadzone = true
|
||||||
|
scroll_damper = dragging_scroll_damper
|
||||||
|
last_scroll_type = SCROLL_TYPE.DRAG
|
||||||
|
init_drag_temp_data()
|
||||||
|
kill_scroll_to_tweens()
|
||||||
|
else:
|
||||||
|
content_dragging = false
|
||||||
|
is_in_deadzone = false
|
||||||
|
# Handle input if handle_input is true
|
||||||
|
if handle_input:
|
||||||
|
get_tree().get_root().set_input_as_handled()
|
||||||
|
|
||||||
|
# Scroll to new focused element
|
||||||
|
func _on_focus_changed(control: Control) -> void:
|
||||||
|
if follow_focus:
|
||||||
|
self.ensure_control_visible(control)
|
||||||
|
|
||||||
|
func _draw() -> void:
|
||||||
|
if debug_mode:
|
||||||
|
draw_debug()
|
||||||
|
|
||||||
|
# Sets default mouse filter for SmoothScroll children to MOUSE_FILTER_PASS
|
||||||
|
func _on_node_added(node: Node) -> void:
|
||||||
|
if node is Control and Engine.is_editor_hint():
|
||||||
|
if is_ancestor_of(node):
|
||||||
|
node.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||||
|
|
||||||
|
func _scrollbar_hide_timer_timeout() -> void:
|
||||||
|
if !any_scroll_bar_dragged():
|
||||||
|
hide_scrollbars()
|
||||||
|
|
||||||
|
func _set_hide_scrollbar_over_time(value: bool) -> bool:
|
||||||
|
if value == false:
|
||||||
|
if scrollbar_hide_timer != null:
|
||||||
|
scrollbar_hide_timer.stop()
|
||||||
|
if scrollbar_show_tween != null:
|
||||||
|
scrollbar_show_tween.kill()
|
||||||
|
if scrollbar_hide_tween != null:
|
||||||
|
scrollbar_hide_tween.kill()
|
||||||
|
get_h_scroll_bar().modulate = Color.WHITE
|
||||||
|
get_v_scroll_bar().modulate = Color.WHITE
|
||||||
|
else:
|
||||||
|
if scrollbar_hide_timer != null and scrollbar_hide_timer.is_inside_tree():
|
||||||
|
scrollbar_hide_timer.start(scrollbar_hide_time)
|
||||||
|
return value
|
||||||
|
|
||||||
|
func _get(property: StringName) -> Variant:
|
||||||
|
match property:
|
||||||
|
"scroll_horizontal":
|
||||||
|
if !content_node: return 0
|
||||||
|
return -int(content_node.position.x)
|
||||||
|
"scroll_vertical":
|
||||||
|
if !content_node: return 0
|
||||||
|
return -int(content_node.position.y)
|
||||||
|
_:
|
||||||
|
return null
|
||||||
|
|
||||||
|
func _set(property: StringName, value: Variant) -> bool:
|
||||||
|
match property:
|
||||||
|
"scroll_horizontal":
|
||||||
|
if !content_node:
|
||||||
|
scroll_horizontal = 0
|
||||||
|
return true
|
||||||
|
scroll_horizontal = value
|
||||||
|
kill_scroll_x_to_tween()
|
||||||
|
velocity.x = 0.0
|
||||||
|
pos.x = clampf(
|
||||||
|
-value as float,
|
||||||
|
-get_child_size_x_diff(content_node, true),
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
"scroll_vertical":
|
||||||
|
if !content_node:
|
||||||
|
scroll_vertical = 0
|
||||||
|
return true
|
||||||
|
scroll_vertical = value
|
||||||
|
kill_scroll_y_to_tween()
|
||||||
|
velocity.y = 0.0
|
||||||
|
pos.y = clampf(
|
||||||
|
-value as float,
|
||||||
|
-get_child_size_y_diff(content_node, true),
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
_:
|
||||||
|
return false
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Scrolling Logic
|
||||||
|
|
||||||
|
func scroll(vertical: bool, axis_velocity: float, axis_pos: float, delta: float):
|
||||||
|
# If no scroll needed, don't apply forces
|
||||||
|
if vertical:
|
||||||
|
if not should_scroll_vertical():
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if not should_scroll_horizontal():
|
||||||
|
return
|
||||||
|
if !scroll_damper: return
|
||||||
|
# Applies counterforces when overdragging
|
||||||
|
if not content_dragging:
|
||||||
|
axis_velocity = handle_overdrag(vertical, axis_velocity, axis_pos, delta)
|
||||||
|
# Move content node by applying velocity
|
||||||
|
var slide_result = scroll_damper.slide(axis_velocity, delta)
|
||||||
|
axis_velocity = slide_result[0]
|
||||||
|
axis_pos += slide_result[1]
|
||||||
|
# Snap to boundary if close enough
|
||||||
|
var snap_result = snap(vertical, axis_velocity, axis_pos)
|
||||||
|
axis_velocity = snap_result[0]
|
||||||
|
axis_pos = snap_result[1]
|
||||||
|
else:
|
||||||
|
# Preserve dragging velocity for 1 frame
|
||||||
|
# in case no movement event while releasing dragging with touch
|
||||||
|
if content_dragging_moved:
|
||||||
|
content_dragging_moved = false
|
||||||
|
else:
|
||||||
|
axis_velocity = 0.0
|
||||||
|
# If using scroll bar dragging, set the content_node's
|
||||||
|
# position by using the scrollbar position
|
||||||
|
if handle_scrollbar_drag():
|
||||||
|
return
|
||||||
|
|
||||||
|
if vertical:
|
||||||
|
if not allow_overdragging:
|
||||||
|
# Clamp if calculated position is beyond boundary
|
||||||
|
if is_outside_top_boundary(axis_pos):
|
||||||
|
axis_pos = 0.0
|
||||||
|
axis_velocity = 0.0
|
||||||
|
elif is_outside_bottom_boundary(axis_pos):
|
||||||
|
axis_pos = -get_child_size_y_diff(content_node, true)
|
||||||
|
axis_velocity = 0.0
|
||||||
|
|
||||||
|
content_node.position.y = axis_pos
|
||||||
|
pos.y = axis_pos
|
||||||
|
velocity.y = axis_velocity
|
||||||
|
else:
|
||||||
|
if not allow_overdragging:
|
||||||
|
# Clamp if calculated position is beyond boundary
|
||||||
|
if is_outside_left_boundary(axis_pos):
|
||||||
|
axis_pos = 0.0
|
||||||
|
axis_velocity = 0.0
|
||||||
|
elif is_outside_right_boundary(axis_pos):
|
||||||
|
axis_pos = -get_child_size_x_diff(content_node, true)
|
||||||
|
axis_velocity = 0.0
|
||||||
|
|
||||||
|
content_node.position.x = axis_pos
|
||||||
|
pos.x = axis_pos
|
||||||
|
velocity.x = axis_velocity
|
||||||
|
|
||||||
|
func handle_overdrag(vertical: bool, axis_velocity: float, axis_pos: float, delta: float) -> float:
|
||||||
|
if !scroll_damper: return 0.0
|
||||||
|
# Calculate the size difference between this container and content_node
|
||||||
|
var size_diff = get_child_size_y_diff(content_node, true) \
|
||||||
|
if vertical else get_child_size_x_diff(content_node, true)
|
||||||
|
# Calculate distance to left and right or top and bottom
|
||||||
|
var dist1 = get_child_top_dist(axis_pos, size_diff) \
|
||||||
|
if vertical else get_child_left_dist(axis_pos, size_diff)
|
||||||
|
var dist2 = get_child_bottom_dist(axis_pos, size_diff) \
|
||||||
|
if vertical else get_child_right_dist(axis_pos, size_diff)
|
||||||
|
# Calculate velocity to left and right or top and bottom
|
||||||
|
var target_vel1 = scroll_damper._calculate_velocity_to_dest(dist1, 0.0)
|
||||||
|
var target_vel2 = scroll_damper._calculate_velocity_to_dest(dist2, 0.0)
|
||||||
|
# Bounce when out of boundary. When velocity is not fast enough to go back,
|
||||||
|
# apply a opposite force and get a new velocity. If the new velocity is too fast,
|
||||||
|
# apply a velocity that makes it scroll back exactly.
|
||||||
|
if axis_pos > 0.0:
|
||||||
|
if axis_velocity > target_vel1:
|
||||||
|
axis_velocity = scroll_damper.attract(
|
||||||
|
dist1,
|
||||||
|
0.0,
|
||||||
|
axis_velocity,
|
||||||
|
delta
|
||||||
|
)
|
||||||
|
if axis_pos < -size_diff:
|
||||||
|
if axis_velocity < target_vel2:
|
||||||
|
axis_velocity = scroll_damper.attract(
|
||||||
|
dist2,
|
||||||
|
0.0,
|
||||||
|
axis_velocity,
|
||||||
|
delta
|
||||||
|
)
|
||||||
|
|
||||||
|
return axis_velocity
|
||||||
|
|
||||||
|
# Snap to boundary if close enough in next frame
|
||||||
|
func snap(vertical: bool, axis_velocity: float, axis_pos: float) -> Array:
|
||||||
|
# Calculate the size difference between this container and content_node
|
||||||
|
var size_diff = get_child_size_y_diff(content_node, true) \
|
||||||
|
if vertical else get_child_size_x_diff(content_node, true)
|
||||||
|
# Calculate distance to left and right or top and bottom
|
||||||
|
var dist1 = get_child_top_dist(axis_pos, size_diff) \
|
||||||
|
if vertical else get_child_left_dist(axis_pos, size_diff)
|
||||||
|
var dist2 = get_child_bottom_dist(axis_pos, size_diff) \
|
||||||
|
if vertical else get_child_right_dist(axis_pos, size_diff)
|
||||||
|
if (
|
||||||
|
dist1 > 0.0 \
|
||||||
|
and abs(dist1) < just_snap_under \
|
||||||
|
and abs(axis_velocity) < just_snap_under \
|
||||||
|
):
|
||||||
|
axis_pos -= dist1
|
||||||
|
axis_velocity = 0.0
|
||||||
|
elif (
|
||||||
|
dist2 < 0.0 \
|
||||||
|
and abs(dist2) < just_snap_under \
|
||||||
|
and abs(axis_velocity) < just_snap_under \
|
||||||
|
):
|
||||||
|
axis_pos -= dist2
|
||||||
|
axis_velocity = 0.0
|
||||||
|
|
||||||
|
return [axis_velocity, axis_pos]
|
||||||
|
|
||||||
|
## Returns true when scrollbar was dragged
|
||||||
|
func handle_scrollbar_drag() -> bool:
|
||||||
|
if h_scrollbar_dragging:
|
||||||
|
velocity.x = 0.0
|
||||||
|
pos.x = -get_h_scroll_bar().value
|
||||||
|
return true
|
||||||
|
|
||||||
|
if v_scrollbar_dragging:
|
||||||
|
velocity.y = 0.0
|
||||||
|
pos.y = -get_v_scroll_bar().value
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
func handle_content_dragging() -> void:
|
||||||
|
if !dragging_scroll_damper: return
|
||||||
|
|
||||||
|
if(
|
||||||
|
Vector2(drag_temp_data[0], drag_temp_data[1]).length() < scroll_deadzone \
|
||||||
|
and is_in_deadzone
|
||||||
|
):
|
||||||
|
return
|
||||||
|
elif is_in_deadzone == true:
|
||||||
|
is_in_deadzone = false
|
||||||
|
drag_temp_data[0] = 0.0
|
||||||
|
drag_temp_data[1] = 0.0
|
||||||
|
|
||||||
|
content_dragging_moved = true
|
||||||
|
|
||||||
|
var calculate_dest = func(delta: float, damping: float) -> float:
|
||||||
|
if delta >= 0.0:
|
||||||
|
return delta / (1 + delta * damping * 0.00001)
|
||||||
|
else:
|
||||||
|
return delta
|
||||||
|
|
||||||
|
var calculate_position = func(
|
||||||
|
temp_dist1: float, # Temp distance
|
||||||
|
temp_dist2: float,
|
||||||
|
temp_relative: float # Event's relative movement accumulation
|
||||||
|
) -> float:
|
||||||
|
if temp_relative + temp_dist1 > 0.0:
|
||||||
|
var delta = min(temp_relative, temp_relative + temp_dist1)
|
||||||
|
var dest = calculate_dest.call(delta, dragging_scroll_damper._attract_factor)
|
||||||
|
return dest - min(0.0, temp_dist1)
|
||||||
|
elif temp_relative + temp_dist2 < 0.0:
|
||||||
|
var delta = max(temp_relative, temp_relative + temp_dist2)
|
||||||
|
var dest = -calculate_dest.call(-delta, dragging_scroll_damper._attract_factor)
|
||||||
|
return dest - max(0.0, temp_dist2)
|
||||||
|
else: return temp_relative
|
||||||
|
|
||||||
|
if should_scroll_vertical():
|
||||||
|
var y_pos = calculate_position.call(
|
||||||
|
drag_temp_data[6], # Temp top_distance
|
||||||
|
drag_temp_data[7], # Temp bottom_distance
|
||||||
|
drag_temp_data[1] # Temp y relative accumulation
|
||||||
|
) + drag_temp_data[3]
|
||||||
|
velocity.y = (y_pos - pos.y) / get_process_delta_time()
|
||||||
|
pos.y = y_pos
|
||||||
|
if should_scroll_horizontal():
|
||||||
|
var x_pos = calculate_position.call(
|
||||||
|
drag_temp_data[4], # Temp left_distance
|
||||||
|
drag_temp_data[5], # Temp right_distance
|
||||||
|
drag_temp_data[0] # Temp x relative accumulation
|
||||||
|
) + drag_temp_data[2]
|
||||||
|
velocity.x = (x_pos - pos.x) / get_process_delta_time()
|
||||||
|
pos.x = x_pos
|
||||||
|
|
||||||
|
func remove_all_children_focus(node: Node) -> void:
|
||||||
|
if node is Control:
|
||||||
|
var control = node as Control
|
||||||
|
control.release_focus()
|
||||||
|
|
||||||
|
for child in node.get_children():
|
||||||
|
remove_all_children_focus(child)
|
||||||
|
|
||||||
|
func update_is_scrolling() -> void:
|
||||||
|
if(
|
||||||
|
(content_dragging and not is_in_deadzone)
|
||||||
|
or any_scroll_bar_dragged()
|
||||||
|
or velocity != Vector2.ZERO
|
||||||
|
):
|
||||||
|
is_scrolling = true
|
||||||
|
else:
|
||||||
|
is_scrolling = false
|
||||||
|
|
||||||
|
func update_scrollbars() -> void:
|
||||||
|
# Update vertical scroll bar
|
||||||
|
if get_v_scroll_bar().value != -pos.y:
|
||||||
|
get_v_scroll_bar().set_value_no_signal(-pos.y)
|
||||||
|
get_v_scroll_bar().queue_redraw()
|
||||||
|
# Update horizontal scroll bar
|
||||||
|
if get_h_scroll_bar().value != -pos.x:
|
||||||
|
get_h_scroll_bar().set_value_no_signal(-pos.x)
|
||||||
|
get_h_scroll_bar().queue_redraw()
|
||||||
|
|
||||||
|
# Always show sroll bars when scrolling or mouse is on any scroll bar
|
||||||
|
if hide_scrollbar_over_time and (is_scrolling or mouse_on_scrollbar):
|
||||||
|
show_scrollbars()
|
||||||
|
|
||||||
|
func init_drag_temp_data() -> void:
|
||||||
|
# Calculate the size difference between this container and content_node
|
||||||
|
var content_node_size_diff = get_child_size_diff(content_node, true, true)
|
||||||
|
# Calculate distance to left, right, top and bottom
|
||||||
|
var content_node_boundary_dist = get_child_boundary_dist(
|
||||||
|
content_node.position,
|
||||||
|
content_node_size_diff
|
||||||
|
)
|
||||||
|
drag_temp_data = [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
content_node.position.x,
|
||||||
|
content_node.position.y,
|
||||||
|
content_node_boundary_dist.x,
|
||||||
|
content_node_boundary_dist.y,
|
||||||
|
content_node_boundary_dist.z,
|
||||||
|
content_node_boundary_dist.w,
|
||||||
|
]
|
||||||
|
|
||||||
|
# Get container size x without v scroll bar 's width
|
||||||
|
func get_spare_size_x() -> float:
|
||||||
|
var size_x = size.x
|
||||||
|
if get_v_scroll_bar().visible:
|
||||||
|
size_x -= get_v_scroll_bar().size.x
|
||||||
|
return max(size_x, 0.0)
|
||||||
|
|
||||||
|
# Get container size y without h scroll bar 's height
|
||||||
|
func get_spare_size_y() -> float:
|
||||||
|
var size_y = size.y
|
||||||
|
if get_h_scroll_bar().visible:
|
||||||
|
size_y -= get_h_scroll_bar().size.y
|
||||||
|
return max(size_y, 0.0)
|
||||||
|
|
||||||
|
# Get container size without scroll bars' size
|
||||||
|
func get_spare_size() -> Vector2:
|
||||||
|
return Vector2(get_spare_size_x(), get_spare_size_y())
|
||||||
|
|
||||||
|
# Calculate the size x difference between this container and child node
|
||||||
|
func get_child_size_x_diff(child: Control, clamp: bool) -> float:
|
||||||
|
var child_size_x = child.size.x * child.scale.x
|
||||||
|
# Falsify the size of the child node to avoid errors
|
||||||
|
# when its size is smaller than this container 's
|
||||||
|
if clamp:
|
||||||
|
child_size_x = max(child_size_x, get_spare_size_x())
|
||||||
|
return child_size_x - get_spare_size_x()
|
||||||
|
|
||||||
|
# Calculate the size y difference between this container and child node
|
||||||
|
func get_child_size_y_diff(child: Control, clamp: bool) -> float:
|
||||||
|
var child_size_y = child.size.y * child.scale.y
|
||||||
|
# Falsify the size of the child node to avoid errors
|
||||||
|
# when its size is smaller than this container 's
|
||||||
|
if clamp:
|
||||||
|
child_size_y = max(child_size_y, get_spare_size_y())
|
||||||
|
return child_size_y - get_spare_size_y()
|
||||||
|
|
||||||
|
# Calculate the size difference between this container and child node
|
||||||
|
func get_child_size_diff(child: Control, clamp_x: bool, clamp_y: bool) -> Vector2:
|
||||||
|
return Vector2(
|
||||||
|
get_child_size_x_diff(child, clamp_x),
|
||||||
|
get_child_size_y_diff(child, clamp_y)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Calculate distance to left
|
||||||
|
func get_child_left_dist(child_pos_x: float, child_size_diff_x: float) -> float:
|
||||||
|
return child_pos_x
|
||||||
|
|
||||||
|
# Calculate distance to right
|
||||||
|
func get_child_right_dist(child_pos_x: float, child_size_diff_x: float) -> float:
|
||||||
|
return child_pos_x + child_size_diff_x
|
||||||
|
|
||||||
|
# Calculate distance to top
|
||||||
|
func get_child_top_dist(child_pos_y: float, child_size_diff_y: float) -> float:
|
||||||
|
return child_pos_y
|
||||||
|
|
||||||
|
# Calculate distance to bottom
|
||||||
|
func get_child_bottom_dist(child_pos_y: float, child_size_diff_y: float) -> float:
|
||||||
|
return child_pos_y + child_size_diff_y
|
||||||
|
|
||||||
|
# Calculate distance to left, right, top and bottom
|
||||||
|
func get_child_boundary_dist(child_pos: Vector2, child_size_diff: Vector2) -> Vector4:
|
||||||
|
return Vector4(
|
||||||
|
get_child_left_dist(child_pos.x, child_size_diff.x),
|
||||||
|
get_child_right_dist(child_pos.x, child_size_diff.x),
|
||||||
|
get_child_top_dist(child_pos.y, child_size_diff.y),
|
||||||
|
get_child_bottom_dist(child_pos.y, child_size_diff.y),
|
||||||
|
)
|
||||||
|
|
||||||
|
func kill_scroll_x_to_tween() -> void:
|
||||||
|
if scroll_x_to_tween: scroll_x_to_tween.kill()
|
||||||
|
|
||||||
|
func kill_scroll_y_to_tween() -> void:
|
||||||
|
if scroll_y_to_tween: scroll_y_to_tween.kill()
|
||||||
|
|
||||||
|
func kill_scroll_to_tweens() -> void:
|
||||||
|
kill_scroll_x_to_tween()
|
||||||
|
kill_scroll_y_to_tween()
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Debug Drawing
|
||||||
|
|
||||||
|
var debug_gradient := Gradient.new()
|
||||||
|
|
||||||
|
func setup_debug_drawing() -> void:
|
||||||
|
debug_gradient.set_color(0.0, Color.GREEN)
|
||||||
|
debug_gradient.set_color(1.0, Color.RED)
|
||||||
|
|
||||||
|
func draw_debug() -> void:
|
||||||
|
# Calculate the size difference between this container and content_node
|
||||||
|
var size_diff = get_child_size_diff(content_node, false, false)
|
||||||
|
# Calculate distance to left, right, top and bottom
|
||||||
|
var boundary_dist = get_child_boundary_dist(
|
||||||
|
content_node.position,
|
||||||
|
size_diff
|
||||||
|
)
|
||||||
|
var bottom_distance = boundary_dist.w
|
||||||
|
var top_distance = boundary_dist.z
|
||||||
|
var right_distance = boundary_dist.y
|
||||||
|
var left_distance = boundary_dist.x
|
||||||
|
# Overdrag lines
|
||||||
|
# Top + Bottom
|
||||||
|
draw_line(Vector2(0.0, 0.0), Vector2(0.0, top_distance), debug_gradient.sample(clamp(top_distance / size.y, 0.0, 1.0)), 5.0)
|
||||||
|
draw_line(Vector2(0.0, size.y), Vector2(0.0, size.y+bottom_distance), debug_gradient.sample(clamp(-bottom_distance / size.y, 0.0, 1.0)), 5.0)
|
||||||
|
# Left + Right
|
||||||
|
draw_line(Vector2(0.0, size.y), Vector2(left_distance, size.y), debug_gradient.sample(clamp(left_distance / size.y, 0.0, 1.0)), 5.0)
|
||||||
|
draw_line(Vector2(size.x, size.y), Vector2(size.x+right_distance, size.y), debug_gradient.sample(clamp(-right_distance / size.y, 0.0, 1.0)), 5.0)
|
||||||
|
|
||||||
|
# Velocity lines
|
||||||
|
var origin := Vector2(5.0, size.y/2)
|
||||||
|
draw_line(origin, origin + Vector2(0.0, velocity.y*0.01), debug_gradient.sample(clamp(velocity.y*2 / size.y, 0.0, 1.0)), 5.0)
|
||||||
|
draw_line(origin, origin + Vector2(0.0, velocity.x*0.01), debug_gradient.sample(clamp(velocity.x*2 / size.x, 0.0, 1.0)), 5.0)
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region API Functions
|
||||||
|
|
||||||
|
## Scrolls to specific x position
|
||||||
|
func scroll_x_to(x_pos: float, duration := 0.5) -> void:
|
||||||
|
if not should_scroll_horizontal(): return
|
||||||
|
if content_dragging: return
|
||||||
|
velocity.x = 0.0
|
||||||
|
var size_x_diff = get_child_size_x_diff(content_node, true)
|
||||||
|
x_pos = clampf(x_pos, -size_x_diff, 0.0)
|
||||||
|
kill_scroll_x_to_tween()
|
||||||
|
scroll_x_to_tween = create_tween()
|
||||||
|
var tweener = scroll_x_to_tween.tween_property(self, "pos:x", x_pos, duration)
|
||||||
|
tweener.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUINT)
|
||||||
|
|
||||||
|
## Scrolls to specific y position
|
||||||
|
func scroll_y_to(y_pos: float, duration := 0.5) -> void:
|
||||||
|
if not should_scroll_vertical(): return
|
||||||
|
if content_dragging: return
|
||||||
|
velocity.y = 0.0
|
||||||
|
var size_y_diff = get_child_size_y_diff(content_node, true)
|
||||||
|
y_pos = clampf(y_pos, -size_y_diff, 0.0)
|
||||||
|
kill_scroll_y_to_tween()
|
||||||
|
scroll_y_to_tween = create_tween()
|
||||||
|
var tweener = scroll_y_to_tween.tween_property(self, "pos:y", y_pos, duration)
|
||||||
|
tweener.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUINT)
|
||||||
|
|
||||||
|
## Scrolls up a page
|
||||||
|
func scroll_page_up(duration := 0.5) -> void:
|
||||||
|
var destination = content_node.position.y + get_spare_size_y()
|
||||||
|
scroll_y_to(destination, duration)
|
||||||
|
|
||||||
|
## Scrolls down a page
|
||||||
|
func scroll_page_down(duration := 0.5) -> void:
|
||||||
|
var destination = content_node.position.y - get_spare_size_y()
|
||||||
|
scroll_y_to(destination, duration)
|
||||||
|
|
||||||
|
## Scrolls left a page
|
||||||
|
func scroll_page_left(duration := 0.5) -> void:
|
||||||
|
var destination = content_node.position.x + get_spare_size_x()
|
||||||
|
scroll_x_to(destination, duration)
|
||||||
|
|
||||||
|
## Scrolls right a page
|
||||||
|
func scroll_page_right(duration := 0.5) -> void:
|
||||||
|
var destination = content_node.position.x - get_spare_size_x()
|
||||||
|
scroll_x_to(destination, duration)
|
||||||
|
|
||||||
|
## Adds velocity to the vertical scroll
|
||||||
|
func scroll_vertically(amount: float) -> void:
|
||||||
|
velocity.y -= amount
|
||||||
|
|
||||||
|
## Adds velocity to the horizontal scroll
|
||||||
|
func scroll_horizontally(amount: float) -> void:
|
||||||
|
velocity.x -= amount
|
||||||
|
|
||||||
|
## Scrolls to top
|
||||||
|
func scroll_to_top(duration := 0.5) -> void:
|
||||||
|
scroll_y_to(0.0, duration)
|
||||||
|
|
||||||
|
## Scrolls to bottom
|
||||||
|
func scroll_to_bottom(duration := 0.5) -> void:
|
||||||
|
scroll_y_to(get_spare_size_y() - content_node.size.y, duration)
|
||||||
|
|
||||||
|
## Scrolls to left
|
||||||
|
func scroll_to_left(duration := 0.5) -> void:
|
||||||
|
scroll_x_to(0.0, duration)
|
||||||
|
|
||||||
|
## Scrolls to right
|
||||||
|
func scroll_to_right(duration := 0.5) -> void:
|
||||||
|
scroll_x_to(get_spare_size_x() - content_node.size.x, duration)
|
||||||
|
|
||||||
|
func is_outside_top_boundary(y_pos: float = pos.y) -> bool:
|
||||||
|
var size_y_diff = get_child_size_y_diff(content_node,true)
|
||||||
|
var top_dist = get_child_top_dist(y_pos, size_y_diff)
|
||||||
|
return top_dist > 0.0
|
||||||
|
|
||||||
|
func is_outside_bottom_boundary(y_pos: float = pos.y) -> bool:
|
||||||
|
var size_y_diff = get_child_size_y_diff(content_node,true)
|
||||||
|
var bottom_dist = get_child_bottom_dist(y_pos, size_y_diff)
|
||||||
|
return bottom_dist < 0.0
|
||||||
|
|
||||||
|
func is_outside_left_boundary(x_pos: float = pos.x) -> bool:
|
||||||
|
var size_x_diff = get_child_size_x_diff(content_node,true)
|
||||||
|
var left_dist = get_child_left_dist(x_pos, size_x_diff)
|
||||||
|
return left_dist > 0.0
|
||||||
|
|
||||||
|
func is_outside_right_boundary(x_pos: float = pos.x) -> bool:
|
||||||
|
var size_x_diff = get_child_size_x_diff(content_node,true)
|
||||||
|
var right_dist = get_child_right_dist(x_pos, size_x_diff)
|
||||||
|
return right_dist < 0.0
|
||||||
|
|
||||||
|
## Returns true if any scroll bar is being dragged
|
||||||
|
func any_scroll_bar_dragged() -> bool:
|
||||||
|
return h_scrollbar_dragging or v_scrollbar_dragging
|
||||||
|
|
||||||
|
## Returns true if there is enough content height to scroll
|
||||||
|
func should_scroll_vertical() -> bool:
|
||||||
|
var disable_scroll = (not allow_vertical_scroll) \
|
||||||
|
or (auto_allow_scroll and get_child_size_y_diff(content_node, false) <= 0) \
|
||||||
|
or !scroll_damper
|
||||||
|
if disable_scroll:
|
||||||
|
velocity.y = 0.0
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
return true
|
||||||
|
|
||||||
|
## Returns true if there is enough content width to scroll
|
||||||
|
func should_scroll_horizontal() -> bool:
|
||||||
|
var disable_scroll = (not allow_horizontal_scroll) \
|
||||||
|
or (auto_allow_scroll and get_child_size_x_diff(content_node, false) <= 0) \
|
||||||
|
or !scroll_damper
|
||||||
|
if disable_scroll:
|
||||||
|
velocity.x = 0.0
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
return true
|
||||||
|
|
||||||
|
## Fades out scrollbars within given [param time].[br]
|
||||||
|
## Default for [param time] is current [member scrollbar_fade_out_time]
|
||||||
|
func hide_scrollbars(time: float = scrollbar_fade_out_time) -> void:
|
||||||
|
# Kill scrollbar_show_tween to avoid animation conflict
|
||||||
|
if scrollbar_show_tween != null and scrollbar_show_tween.is_valid():
|
||||||
|
scrollbar_show_tween.kill()
|
||||||
|
# Create new tweens if needed
|
||||||
|
if (
|
||||||
|
get_v_scroll_bar().modulate != Color.TRANSPARENT \
|
||||||
|
or get_h_scroll_bar().modulate != Color.TRANSPARENT
|
||||||
|
):
|
||||||
|
if scrollbar_hide_tween and !scrollbar_hide_tween.is_running():
|
||||||
|
scrollbar_hide_tween.kill()
|
||||||
|
if scrollbar_hide_tween == null or !scrollbar_hide_tween.is_valid():
|
||||||
|
scrollbar_hide_tween = create_tween()
|
||||||
|
scrollbar_hide_tween.set_parallel(true)
|
||||||
|
scrollbar_hide_tween.tween_property(get_v_scroll_bar(), 'modulate', Color.TRANSPARENT, time)
|
||||||
|
scrollbar_hide_tween.tween_property(get_h_scroll_bar(), 'modulate', Color.TRANSPARENT, time)
|
||||||
|
|
||||||
|
## Fades in scrollbars within given [param time].[br]
|
||||||
|
## Default for [param time] is current [member scrollbar_fade_in_time]
|
||||||
|
func show_scrollbars(time: float = scrollbar_fade_in_time) -> void:
|
||||||
|
# Restart timer
|
||||||
|
scrollbar_hide_timer.start(scrollbar_hide_time)
|
||||||
|
# Kill scrollbar_hide_tween to avoid animation conflict
|
||||||
|
if scrollbar_hide_tween != null and scrollbar_hide_tween.is_valid():
|
||||||
|
scrollbar_hide_tween.kill()
|
||||||
|
# Create new tweens if needed
|
||||||
|
if (
|
||||||
|
get_v_scroll_bar().modulate != Color.WHITE \
|
||||||
|
or get_h_scroll_bar().modulate != Color.WHITE \
|
||||||
|
):
|
||||||
|
if scrollbar_show_tween and !scrollbar_show_tween.is_running():
|
||||||
|
scrollbar_show_tween.kill()
|
||||||
|
if scrollbar_show_tween == null or !scrollbar_show_tween.is_valid():
|
||||||
|
scrollbar_show_tween = create_tween()
|
||||||
|
scrollbar_show_tween.set_parallel(true)
|
||||||
|
scrollbar_show_tween.tween_property(get_v_scroll_bar(), 'modulate', Color.WHITE, time)
|
||||||
|
scrollbar_show_tween.tween_property(get_h_scroll_bar(), 'modulate', Color.WHITE, time)
|
||||||
|
|
||||||
|
## Scroll to position to ensure the given control node is visible
|
||||||
|
func ensure_control_visible(control: Control) -> void:
|
||||||
|
if !content_node: return
|
||||||
|
if !content_node.is_ancestor_of(control): return
|
||||||
|
if !scroll_damper: return
|
||||||
|
|
||||||
|
var size_diff = (
|
||||||
|
control.get_global_rect().size - get_global_rect().size
|
||||||
|
) / (get_global_rect().size / size)
|
||||||
|
var boundary_dist = get_child_boundary_dist(
|
||||||
|
(control.global_position - global_position) \
|
||||||
|
/ (get_global_rect().size / size),
|
||||||
|
size_diff
|
||||||
|
)
|
||||||
|
var content_node_position = content_node.position
|
||||||
|
if boundary_dist.x < 0 + follow_focus_margin:
|
||||||
|
scroll_x_to(content_node_position.x - boundary_dist.x + follow_focus_margin)
|
||||||
|
elif boundary_dist.y > 0 - follow_focus_margin:
|
||||||
|
scroll_x_to(content_node_position.x - boundary_dist.y - follow_focus_margin)
|
||||||
|
if boundary_dist.z < 0 + follow_focus_margin:
|
||||||
|
scroll_y_to(content_node_position.y - boundary_dist.z + follow_focus_margin)
|
||||||
|
elif boundary_dist.w > 0 - follow_focus_margin:
|
||||||
|
scroll_y_to(content_node_position.y - boundary_dist.w - follow_focus_margin)
|
||||||
|
|
||||||
|
#endregion
|
||||||
1
addons/SmoothScroll/SmoothScrollContainer.gd.uid
Normal file
1
addons/SmoothScroll/SmoothScrollContainer.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bgqglerkcylxx
|
||||||
19
addons/SmoothScroll/class-icon.svg
Normal file
19
addons/SmoothScroll/class-icon.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M58.4,10.7C66.4,10.7 73,17.2 73,25.3L73,66.3C73,79 62.7,89.3 50,89.3C37.3,89.3 27,79 27,66.3L27,25.3C27,17.3 33.5,10.7 41.6,10.7L58.4,10.7M58.4,5L41.6,5C30.4,5 21.3,14.1 21.3,25.3L21.3,66.3C21.3,82.1 34.1,95 50,95C65.9,95 78.7,82.1 78.7,66.3L78.7,25.3C78.7,14.1 69.6,5 58.4,5Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50,52.5C49.7,52.5 49.3,52.4 49,52.2L40.2,46C39.4,45.5 39.2,44.4 39.8,43.6C40.3,42.8 41.4,42.6 42.2,43.2L50,48.7L57.8,43.2C58.6,42.7 59.6,42.8 60.2,43.6C60.7,44.4 60.6,45.4 59.8,46L51,52.2C50.7,52.4 50.3,52.5 50,52.5Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50.1,65.8C49.8,65.8 49.4,65.7 49.1,65.5L40.3,59.3C39.5,58.8 39.3,57.7 39.9,56.9C40.4,56.1 41.5,55.9 42.3,56.5L50.1,62L57.9,56.5C58.7,56 59.7,56.1 60.3,56.9C60.8,57.7 60.7,58.7 59.9,59.3L51.1,65.5C50.8,65.7 50.5,65.8 50.1,65.8Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50,78.8C49.7,78.8 49.3,78.7 49,78.5L40.2,72.3C39.4,71.8 39.2,70.7 39.8,69.9C40.4,69.1 41.4,68.9 42.2,69.5L50,75L57.8,69.5C58.6,69 59.6,69.1 60.2,69.9C60.7,70.7 60.6,71.7 59.8,72.3L51,78.5C50.7,78.7 50.3,78.8 50,78.8Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50,36.3C48.1,36.3 46.6,34.8 46.6,32.9L46.6,25.4C46.6,23.5 48.1,22 50,22C51.9,22 53.4,23.5 53.4,25.4L53.4,32.9C53.4,34.8 51.9,36.3 50,36.3Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
37
addons/SmoothScroll/class-icon.svg.import
Normal file
37
addons/SmoothScroll/class-icon.svg.import
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dorhyoghxkay6"
|
||||||
|
path="res://.godot/imported/class-icon.svg-c17de51589a7d30572bf401526524f64.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/SmoothScroll/class-icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/class-icon.svg-c17de51589a7d30572bf401526524f64.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
3
addons/SmoothScroll/debug_font.tres
Normal file
3
addons/SmoothScroll/debug_font.tres
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[gd_resource type="SystemFont" format=3 uid="uid://cw8c0p3b5mv5y"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
BIN
addons/SmoothScroll/icon.afdesign
Normal file
BIN
addons/SmoothScroll/icon.afdesign
Normal file
Binary file not shown.
8
addons/SmoothScroll/plugin.cfg
Normal file
8
addons/SmoothScroll/plugin.cfg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="SmoothScroll"
|
||||||
|
description="""This plugin adds a new scroll container class
|
||||||
|
with additional smooth scroll options."""
|
||||||
|
author="Fabian Keßler (SpyrexDE)"
|
||||||
|
version="1.3"
|
||||||
|
script="plugin.gd"
|
||||||
12
addons/SmoothScroll/plugin.gd
Normal file
12
addons/SmoothScroll/plugin.gd
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
|
||||||
|
func _enter_tree():
|
||||||
|
add_custom_type("ScrollDamper", "Resource", preload("scroll_damper/scroll_damper.gd"), preload("scroll_damper/icon.svg"))
|
||||||
|
add_custom_type("SmoothScrollContainer", "ScrollContainer", preload("SmoothScrollContainer.gd"), preload("class-icon.svg"))
|
||||||
|
|
||||||
|
|
||||||
|
func _exit_tree():
|
||||||
|
remove_custom_type("ScrollDamper")
|
||||||
|
remove_custom_type("SmoothScrollContainer")
|
||||||
1
addons/SmoothScroll/plugin.gd.uid
Normal file
1
addons/SmoothScroll/plugin.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b7svd7w4nwpch
|
||||||
33
addons/SmoothScroll/scroll_damper/cubic_scroll_damper.gd
Normal file
33
addons/SmoothScroll/scroll_damper/cubic_scroll_damper.gd
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
extends ScrollDamper
|
||||||
|
class_name CubicScrollDamper
|
||||||
|
|
||||||
|
|
||||||
|
## Friction, not physical.
|
||||||
|
## The higher the value, the more obvious the deceleration.
|
||||||
|
@export_range(0.001, 10000.0, 0.001, "or_greater", "hide_slider")
|
||||||
|
var friction := 4.0:
|
||||||
|
set(val):
|
||||||
|
friction = max(val, 0.001)
|
||||||
|
_factor = pow(10.0, friction) - 1.0
|
||||||
|
|
||||||
|
## Factor to use in formula
|
||||||
|
var _factor := 10000.0:
|
||||||
|
set(val): _factor = max(val, 0.000000000001)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_velocity_by_time(time: float) -> float:
|
||||||
|
if time <= 0.0: return 0.0
|
||||||
|
return time*time*time * _factor
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_velocity(velocity: float) -> float:
|
||||||
|
return pow(abs(velocity) / _factor, 1.0/3.0)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_offset_by_time(time: float) -> float:
|
||||||
|
time = max(time, 0.0)
|
||||||
|
return 1.0/4.0 * _factor * time*time*time*time
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_offset(offset: float) -> float:
|
||||||
|
return pow(abs(offset) * 4.0 / _factor, 1.0/4.0)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dlagxcnf11lr4
|
||||||
47
addons/SmoothScroll/scroll_damper/expo_scroll_damper.gd
Normal file
47
addons/SmoothScroll/scroll_damper/expo_scroll_damper.gd
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
extends ScrollDamper
|
||||||
|
class_name ExpoScrollDamper
|
||||||
|
|
||||||
|
|
||||||
|
## Friction, not physical.
|
||||||
|
## The higher the value, the more obvious the deceleration.
|
||||||
|
@export_range(0.001, 10000.0, 0.001, "or_greater", "hide_slider")
|
||||||
|
var friction := 4.0:
|
||||||
|
set(val):
|
||||||
|
friction = max(val, 0.001)
|
||||||
|
_factor = pow(10.0, friction)
|
||||||
|
|
||||||
|
## Factor to use in formula
|
||||||
|
var _factor := 10000.0:
|
||||||
|
set(val): _factor = max(val, 1.000000000001)
|
||||||
|
|
||||||
|
## Minumun velocity.
|
||||||
|
@export_range(0.001, 100000.0, 0.001, "or_greater", "hide_slider")
|
||||||
|
var minimum_velocity := 0.4:
|
||||||
|
set(val): minimum_velocity = max(val, 0.001)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_velocity_by_time(time: float) -> float:
|
||||||
|
var minimum_time = _calculate_time_by_velocity(minimum_velocity)
|
||||||
|
if time <= minimum_time: return 0.0
|
||||||
|
return pow(_factor, time)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_velocity(velocity: float) -> float:
|
||||||
|
return log(abs(velocity)) / log(_factor)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_offset_by_time(time: float) -> float:
|
||||||
|
return pow(_factor, time) / log(_factor)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_offset(offset: float) -> float:
|
||||||
|
return log(offset * log(_factor)) / log(_factor)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_velocity_to_dest(from: float, to: float) -> float:
|
||||||
|
var dist = to - from
|
||||||
|
var min_time = _calculate_time_by_velocity(minimum_velocity)
|
||||||
|
var min_offset = _calculate_offset_by_time(min_time)
|
||||||
|
var time = _calculate_time_by_offset(abs(dist) + min_offset)
|
||||||
|
var vel = _calculate_velocity_by_time(time) * sign(dist)
|
||||||
|
return vel
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://b7h0k2h2qwlqv
|
||||||
BIN
addons/SmoothScroll/scroll_damper/icon.afdesign
Normal file
BIN
addons/SmoothScroll/scroll_damper/icon.afdesign
Normal file
Binary file not shown.
29
addons/SmoothScroll/scroll_damper/icon.svg
Normal file
29
addons/SmoothScroll/scroll_damper/icon.svg
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(1,0,0,1,34.4806,-89.0415)">
|
||||||
|
<path d="M185.254,992.427C601.296,991.628 768.576,997.711 778.133,294.568" style="fill:none;stroke:rgb(191,244,196);stroke-width:70px;"/>
|
||||||
|
</g>
|
||||||
|
<g id="icon.afdesign" transform="matrix(28.1622,0,0,28.1622,387.945,401.774)">
|
||||||
|
<g transform="matrix(1,0,0,1,-12.5,-12.5)">
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M58.4,10.7C66.4,10.7 73,17.2 73,25.3L73,66.3C73,79 62.7,89.3 50,89.3C37.3,89.3 27,79 27,66.3L27,25.3C27,17.3 33.5,10.7 41.6,10.7L58.4,10.7M58.4,5L41.6,5C30.4,5 21.3,14.1 21.3,25.3L21.3,66.3C21.3,82.1 34.1,95 50,95C65.9,95 78.7,82.1 78.7,66.3L78.7,25.3C78.7,14.1 69.6,5 58.4,5Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50,52.5C49.7,52.5 49.3,52.4 49,52.2L40.2,46C39.4,45.5 39.2,44.4 39.8,43.6C40.3,42.8 41.4,42.6 42.2,43.2L50,48.7L57.8,43.2C58.6,42.7 59.6,42.8 60.2,43.6C60.7,44.4 60.6,45.4 59.8,46L51,52.2C50.7,52.4 50.3,52.5 50,52.5Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50.1,65.8C49.8,65.8 49.4,65.7 49.1,65.5L40.3,59.3C39.5,58.8 39.3,57.7 39.9,56.9C40.4,56.1 41.5,55.9 42.3,56.5L50.1,62L57.9,56.5C58.7,56 59.7,56.1 60.3,56.9C60.8,57.7 60.7,58.7 59.9,59.3L51.1,65.5C50.8,65.7 50.5,65.8 50.1,65.8Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50,78.8C49.7,78.8 49.3,78.7 49,78.5L40.2,72.3C39.4,71.8 39.2,70.7 39.8,69.9C40.4,69.1 41.4,68.9 42.2,69.5L50,75L57.8,69.5C58.6,69 59.6,69.1 60.2,69.9C60.7,70.7 60.6,71.7 59.8,72.3L51,78.5C50.7,78.7 50.3,78.8 50,78.8Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.268293,0,0,0.255556,-0.914634,-0.277778)">
|
||||||
|
<path d="M50,36.3C48.1,36.3 46.6,34.8 46.6,32.9L46.6,25.4C46.6,23.5 48.1,22 50,22C51.9,22 53.4,23.5 53.4,25.4L53.4,32.9C53.4,34.8 51.9,36.3 50,36.3Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(-10.6313,1.88738e-15,-3.10862e-15,-25.7598,1344.05,1429.95)">
|
||||||
|
<path d="M50,52.5C49.7,52.5 49.3,52.4 49,52.2L40.2,46C39.4,45.5 39.2,44.4 39.8,43.6C40.3,42.8 41.4,42.6 42.2,43.2L50,48.7L57.8,43.2C58.6,42.7 59.6,42.8 60.2,43.6C60.7,44.4 60.6,45.4 59.8,46L51,52.2C50.7,52.4 50.3,52.5 50,52.5Z" style="fill:rgb(191,244,196);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
37
addons/SmoothScroll/scroll_damper/icon.svg.import
Normal file
37
addons/SmoothScroll/scroll_damper/icon.svg.import
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://4ok12qtgl7xq"
|
||||||
|
path="res://.godot/imported/icon.svg-5b01e8115f19d6d63dc265815ce29c75.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/SmoothScroll/scroll_damper/icon.svg"
|
||||||
|
dest_files=["res://.godot/imported/icon.svg-5b01e8115f19d6d63dc265815ce29c75.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
33
addons/SmoothScroll/scroll_damper/linear_scroll_damper.gd
Normal file
33
addons/SmoothScroll/scroll_damper/linear_scroll_damper.gd
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
extends ScrollDamper
|
||||||
|
class_name LinearScrollDamper
|
||||||
|
|
||||||
|
|
||||||
|
## Friction, not physical.
|
||||||
|
## The higher the value, the more obvious the deceleration.
|
||||||
|
@export_range(0.001, 10000.0, 0.001, "or_greater", "hide_slider")
|
||||||
|
var friction := 4.0:
|
||||||
|
set(val):
|
||||||
|
friction = max(val, 0.001)
|
||||||
|
_factor = pow(10.0, friction) - 1.0
|
||||||
|
|
||||||
|
## Factor to use in formula
|
||||||
|
var _factor := 10000.0:
|
||||||
|
set(val): _factor = max(val, 0.000000000001)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_velocity_by_time(time: float) -> float:
|
||||||
|
if time <= 0.0: return 0.0
|
||||||
|
return time * _factor
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_velocity(velocity: float) -> float:
|
||||||
|
return abs(velocity) / _factor
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_offset_by_time(time: float) -> float:
|
||||||
|
time = max(time, 0.0)
|
||||||
|
return 1.0/2.0 * _factor * time*time
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_offset(offset: float) -> float:
|
||||||
|
return sqrt(abs(offset) * 2.0 / _factor)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dvvc75dvopgcg
|
||||||
33
addons/SmoothScroll/scroll_damper/quad_scroll_damper.gd
Normal file
33
addons/SmoothScroll/scroll_damper/quad_scroll_damper.gd
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
extends ScrollDamper
|
||||||
|
class_name QuadScrollDamper
|
||||||
|
|
||||||
|
|
||||||
|
## Friction, not physical.
|
||||||
|
## The higher the value, the more obvious the deceleration.
|
||||||
|
@export_range(0.001, 10000.0, 0.001, "or_greater", "hide_slider")
|
||||||
|
var friction := 4.0:
|
||||||
|
set(val):
|
||||||
|
friction = max(val, 0.001)
|
||||||
|
_factor = pow(10.0, friction) - 1.0
|
||||||
|
|
||||||
|
## Factor to use in formula
|
||||||
|
var _factor := 10000.0:
|
||||||
|
set(val): _factor = max(val, 0.000000000001)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_velocity_by_time(time: float) -> float:
|
||||||
|
if time <= 0.0: return 0.0
|
||||||
|
return time*time * _factor
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_velocity(velocity: float) -> float:
|
||||||
|
return sqrt(abs(velocity) / _factor)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_offset_by_time(time: float) -> float:
|
||||||
|
time = max(time, 0.0)
|
||||||
|
return 1.0/3.0 * _factor * time*time*time
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_time_by_offset(offset: float) -> float:
|
||||||
|
return pow(abs(offset) * 3.0 / _factor, 1.0/3.0)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://ccgjrqkk3oksk
|
||||||
74
addons/SmoothScroll/scroll_damper/scroll_damper.gd
Normal file
74
addons/SmoothScroll/scroll_damper/scroll_damper.gd
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
@icon("icon.svg")
|
||||||
|
extends Resource
|
||||||
|
class_name ScrollDamper
|
||||||
|
|
||||||
|
## Abstract class
|
||||||
|
|
||||||
|
## Rebound strength. The higher the value, the faster it attracts.
|
||||||
|
@export_range(0.0, 1.0, 0.001, "or_greater", "hide_slider")
|
||||||
|
var rebound_strength := 7.0:
|
||||||
|
set(val):
|
||||||
|
rebound_strength= max(val, 0.0)
|
||||||
|
_attract_factor = rebound_strength * rebound_strength * rebound_strength
|
||||||
|
|
||||||
|
## Factor for attracting.
|
||||||
|
var _attract_factor := 400.0:
|
||||||
|
set(val):
|
||||||
|
_attract_factor = max(val, 0.0)
|
||||||
|
|
||||||
|
|
||||||
|
# Abstract method
|
||||||
|
func _calculate_velocity_by_time(time: float) -> float:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
# Abstract method
|
||||||
|
func _calculate_time_by_velocity(velocity: float) -> float:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
# Abstract method
|
||||||
|
func _calculate_offset_by_time(time: float) -> float:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
# Abstract method
|
||||||
|
func _calculate_time_by_offset(offset: float) -> float:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_velocity_to_dest(from: float, to: float) -> float:
|
||||||
|
var dist = to - from
|
||||||
|
var time = _calculate_time_by_offset(abs(dist))
|
||||||
|
var vel = _calculate_velocity_by_time(time) * sign(dist)
|
||||||
|
return vel
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_next_velocity(present_time: float, delta_time: float) -> float:
|
||||||
|
return _calculate_velocity_by_time(present_time - delta_time)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_next_offset(present_time: float, delta_time: float) -> float:
|
||||||
|
return _calculate_offset_by_time(present_time) \
|
||||||
|
- _calculate_offset_by_time(present_time - delta_time)
|
||||||
|
|
||||||
|
|
||||||
|
## Return the result of next velocity and position according to delta time
|
||||||
|
func slide(velocity: float, delta_time: float) -> Array:
|
||||||
|
var present_time = _calculate_time_by_velocity(velocity)
|
||||||
|
return [
|
||||||
|
_calculate_next_velocity(present_time, delta_time) * sign(velocity),
|
||||||
|
_calculate_next_offset(present_time, delta_time) * sign(velocity)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
## Emulate force that attracts something to destination.
|
||||||
|
## Return the result of next velocity according to delta time
|
||||||
|
func attract(from: float, to: float, velocity: float, delta_time: float) -> float:
|
||||||
|
var dist = to - from
|
||||||
|
var target_vel = _calculate_velocity_to_dest(from, to)
|
||||||
|
velocity += _attract_factor * dist * delta_time \
|
||||||
|
+ _calculate_velocity_by_time(delta_time) * sign(dist)
|
||||||
|
if (
|
||||||
|
(dist > 0 and velocity >= target_vel) \
|
||||||
|
or (dist < 0 and velocity <= target_vel) \
|
||||||
|
):
|
||||||
|
velocity = target_vel
|
||||||
|
return velocity
|
||||||
1
addons/SmoothScroll/scroll_damper/scroll_damper.gd.uid
Normal file
1
addons/SmoothScroll/scroll_damper/scroll_damper.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bcy471w0pi8af
|
||||||
@@ -26,6 +26,10 @@ window/size/viewport_width=1920
|
|||||||
window/size/viewport_height=1080
|
window/size/viewport_height=1080
|
||||||
window/stretch/aspect="ignore"
|
window/stretch/aspect="ignore"
|
||||||
|
|
||||||
|
[editor_plugins]
|
||||||
|
|
||||||
|
enabled=PackedStringArray("res://addons/SmoothScroll/plugin.cfg")
|
||||||
|
|
||||||
[file_customization]
|
[file_customization]
|
||||||
|
|
||||||
folder_colors={
|
folder_colors={
|
||||||
|
|||||||
Reference in New Issue
Block a user