diff --git a/README.md b/README.md index 2c50fab..f45ef62 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ TODO: 7. **Video** support via [GDE GoZen](https://github.com/VoylinsGamedevJourney/gde_gozen) 8. **More input types** (url, tel, date, time, etc.) 9. **Required** attribute for inputs +10. Installer should register **gurt://** as a valid protocol thru the registry. Issues: 1. **< br />** counts as 1 element in **WebsiteContainer**, therefore despite being (0,0) in size, it counts as double in spacing diff --git a/Scenes/Tab.tscn b/Scenes/Tab.tscn index b97d65f..20e6c0c 100644 --- a/Scenes/Tab.tscn +++ b/Scenes/Tab.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=12 format=3 uid="uid://sqhcxhcre081"] +[gd_scene load_steps=14 format=3 uid="uid://sqhcxhcre081"] [ext_resource type="Script" uid="uid://crpnnfqm3k5xv" path="res://Scripts/Tab.gd" id="1_q3baj"] [ext_resource type="Texture2D" uid="uid://gq8g7t4s3ryg" path="res://Assets/Icons/x.svg" id="2_pisds"] @@ -14,25 +14,95 @@ [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_344ge"] +[sub_resource type="Animation" id="Animation_ib6pj"] +resource_name = "appear" +length = 0.3 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:custom_minimum_size:x") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 350, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/1/type = "bezier" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Button:size:x") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 350, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/2/type = "bezier" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Button/GradientTexture:position:x") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(-64, -0.25, 0, 0.25, 0, 278, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/3/type = "bezier" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Button/CloseButton:position:x") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(-34, -0.25, 0, 0.25, 0, 319, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/4/type = "bezier" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("Button/Icon:position:x") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(-23, -0.25, 0, 0.25, 0, 8, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 0.3) +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Button:clip_contents") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_170sd"] +_data = { +&"appear": SubResource("Animation_ib6pj") +} + [node name="Tab1" type="Control"] custom_minimum_size = Vector2(350, 50) layout_mode = 3 anchors_preset = 0 +offset_right = 350.0 +offset_bottom = 50.0 script = ExtResource("1_q3baj") -[node name="GradientTexture" type="TextureRect" parent="."] -z_index = 1 -layout_mode = 0 -offset_left = 278.0 -offset_top = 2.0 -offset_right = 342.0 -offset_bottom = 50.0 -texture = ExtResource("3_q3baj") - [node name="Button" type="Button" parent="."] +unique_name_in_owner = true z_index = -2 -custom_minimum_size = Vector2(350, 50) -layout_mode = 2 +clip_contents = true +layout_mode = 1 offset_right = 350.0 offset_bottom = 50.0 focus_mode = 0 @@ -50,7 +120,18 @@ icon = ExtResource("5_ib6pj") alignment = 0 text_overrun_behavior = 3 -[node name="Icon" type="TextureRect" parent="."] +[node name="GradientTexture" type="TextureRect" parent="Button"] +unique_name_in_owner = true +z_index = 1 +layout_mode = 0 +offset_left = 278.0 +offset_top = 2.0 +offset_right = 342.0 +offset_bottom = 50.0 +texture = ExtResource("3_q3baj") + +[node name="Icon" type="TextureRect" parent="Button"] +unique_name_in_owner = true custom_minimum_size = Vector2(23, 23) layout_mode = 0 offset_left = 8.0 @@ -60,7 +141,8 @@ offset_bottom = 36.0 texture = ExtResource("6_ib6pj") expand_mode = 1 -[node name="CloseButton" type="Button" parent="."] +[node name="CloseButton" type="Button" parent="Button"] +unique_name_in_owner = true z_index = 2 custom_minimum_size = Vector2(34, 34) layout_mode = 0 @@ -79,6 +161,12 @@ theme_override_styles/normal = ExtResource("7_1ohlo") icon = ExtResource("2_pisds") icon_alignment = 1 +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_170sd") +} +speed_scale = 2.0 + [connection signal="mouse_entered" from="Button" to="." method="_on_button_mouse_entered"] [connection signal="mouse_exited" from="Button" to="." method="_on_button_mouse_exited"] [connection signal="pressed" from="Button" to="." method="_on_button_pressed"] diff --git a/Scripts/Tab.gd b/Scripts/Tab.gd index 49ebbd2..d346ffa 100644 --- a/Scripts/Tab.gd +++ b/Scripts/Tab.gd @@ -4,10 +4,11 @@ extends Control signal tab_pressed signal tab_closed -@onready var gradient_texture: TextureRect = $GradientTexture -@onready var button: Button = $Button -@onready var close_button: Button = $CloseButton -@onready var icon: TextureRect = $Icon +@onready var gradient_texture: TextureRect = %GradientTexture +@onready var button: Button = %Button +@onready var close_button: Button = %CloseButton +@onready var icon: TextureRect = %Icon +@onready var animation: AnimationPlayer = $AnimationPlayer const TAB_GRADIENT: GradientTexture2D = preload("res://Scenes/Styles/TabGradient.tres") const TAB_GRADIENT_DEFAULT: GradientTexture2D = preload("res://Scenes/Styles/TabGradientDefault.tres") @@ -20,6 +21,7 @@ const CLOSE_BUTTON_NORMAL: StyleBoxFlat = preload("res://Scenes/Styles/CloseButt var is_active := false var mouse_over_tab := false +var loading_tween: Tween func _ready(): add_to_group("tabs") @@ -44,6 +46,32 @@ func set_icon(new_icon: Texture) -> void: icon.texture = new_icon icon.rotation = 0 +func update_icon_from_url(icon_url: String) -> void: + const LOADER_CIRCLE = preload("res://Assets/Icons/loader-circle.svg") + + loading_tween = create_tween() + + set_icon(LOADER_CIRCLE) + + loading_tween.set_loops() + + icon.pivot_offset = Vector2(11.5, 11.5) + loading_tween.tween_method(func(angle): + if !is_instance_valid(icon): + if loading_tween: loading_tween.kill() + return + icon.rotation = angle + , 0.0, TAU, 1.0) + + var icon_resource = await Network.fetch_image(icon_url) + + # Only update if tab still exists + if is_instance_valid(self): + set_icon(icon_resource) + if loading_tween: + loading_tween.kill() + loading_tween = null + func _on_button_mouse_entered() -> void: mouse_over_tab = true if is_active: return @@ -55,6 +83,9 @@ func _on_button_mouse_exited() -> void: gradient_texture.texture = TAB_GRADIENT_DEFAULT func _exit_tree(): + if loading_tween: + loading_tween.kill() + loading_tween = null remove_from_group("tabs") func _on_button_pressed() -> void: @@ -70,4 +101,6 @@ func _on_button_pressed() -> void: func _on_close_button_pressed() -> void: tab_closed.emit() + animation.play("appear", -1, -1.0, true) + await animation.animation_finished queue_free() diff --git a/Scripts/TabContainer.gd b/Scripts/TabContainer.gd index a86ca4e..68b1b9c 100644 --- a/Scripts/TabContainer.gd +++ b/Scripts/TabContainer.gd @@ -79,8 +79,10 @@ func create_tab() -> void: tabs.append(tab) tab.tab_pressed.connect(_tab_pressed.bind(index)) tab.tab_closed.connect(_tab_closed.bind(index)) - h_box_container.add_child(tab) + h_box_container.add_child(tab) + tab.animation.play("appear") + set_active_tab(index) # WARNING: temporary diff --git a/Scripts/main.gd b/Scripts/main.gd index 6de0a45..126ff01 100644 --- a/Scripts/main.gd +++ b/Scripts/main.gd @@ -5,8 +5,6 @@ extends Control @onready var tab_container: TabManager = $VBoxContainer/TabContainer const LOADER_CIRCLE = preload("res://Assets/Icons/loader-circle.svg") -var loading_tween: Tween - const P = preload("res://Scenes/Tags/p.tscn") const IMG = preload("res://Scenes/Tags/img.tscn") const SEPARATOR = preload("res://Scenes/Tags/separator.tscn") @@ -122,8 +120,7 @@ line breaks tab.set_title(title) var icon = parser.get_icon() - set_loading_icon(tab) - call_deferred("update_tab_icon", tab, icon) + tab.update_icon_from_url(icon) var body = parser.find_first("body") var i = 0 @@ -165,25 +162,6 @@ line breaks i += 1 -func set_loading_icon(tab: Tab) -> void: - tab.set_icon(LOADER_CIRCLE) - - loading_tween = create_tween() - loading_tween.set_loops() - - var icon = tab.icon - icon.pivot_offset = Vector2(11.5, 11.5) - loading_tween.tween_method(func(angle): icon.rotation = angle, 0.0, TAU, 1.0) - -func stop_loading_icon() -> void: - if loading_tween: - loading_tween.kill() - loading_tween = null - -func update_tab_icon(tab: Tab, icon: String) -> void: - tab.set_icon(await Network.fetch_image(icon)) - stop_loading_icon() - func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool: if element.tag_name == "a": return true