1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 09:09:47 +00:00

Recorder extension to core (#823)

* Add files via upload

* add fps counter

* formatting

* Update Recorder.gd

* Some improvements

* typo

* another typo

* formatting
This commit is contained in:
Variable 2023-03-07 19:21:03 +05:00 committed by GitHub
parent e88ba2cb73
commit d9efe97bfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 653 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/start.png-f7a4d6649bfd0c2da16e274157bff38e.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/recorder/start.png"
dest_files=[ "res://.import/start.png-f7a4d6649bfd0c2da16e274157bff38e.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=false
svg/scale=1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

View file

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/stop.png-17c7b37f77b393ec6d5202f1f8a192ce.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/graphics/recorder/stop.png"
dest_files=[ "res://.import/stop.png-17c7b37f77b393ec6d5202f1f8a192ce.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=false
svg/scale=1.0

199
src/UI/Recorder/Recorder.gd Normal file
View file

@ -0,0 +1,199 @@
extends PanelContainer
signal frame_saved
enum Mode { CANVAS, PIXELORAMA }
var mode = 0
var chosen_dir = ""
var save_dir = ""
var project: Project
var cache: Array = [] # Array of images stored during recording
var frame_captured = 0 # A variable used to visualize frames captured
var skip_amount = 1 # No of "do" actions after which a frame can be captured
var current_frame_no = 0 # used to compare with skip_amount to see if it can be captured
var resize := 100
onready var project_list = $"%TargetProjectOption"
onready var folder_button: Button = $"%Folder"
onready var start_button = $"%Start"
onready var size: Label = $"%Size"
onready var path_field = $"%Path"
func _ready() -> void:
refresh_projects_list()
project = Global.current_project
connect("frame_saved", self, "_on_frame_saved")
# Make a recordings folder if there isn't one
var dir = Directory.new()
chosen_dir = Global.directory_module.xdg_data_home.plus_file("Recordings")
dir.make_dir_recursive(chosen_dir)
path_field.text = chosen_dir
size.text = str("(", project.size.x, "×", project.size.y, ")")
func initialize_recording():
connect_undo() # connect to detect changes in project
cache.clear() # clear the cache array to store new images
frame_captured = 0
current_frame_no = skip_amount - 1
# disable some options that are not required during recording
folder_button.visible = true
project_list.visible = false
$ScrollContainer/CenterContainer/GridContainer/Captured.visible = true
for child in $Dialogs/Options/PanelContainer/VBoxContainer.get_children():
if !child.is_in_group("visible during recording"):
child.visible = false
save_dir = chosen_dir
# Remove end back-slashes if present
if save_dir.ends_with("/"):
save_dir[-1] = ""
# Create a new directory based on time
var folder = str(
project.name, OS.get_time().hour, "_", OS.get_time().minute, "_", OS.get_time().second
)
save_dir = save_dir.plus_file(folder)
var dir := Directory.new()
# warning-ignore:return_value_discarded
dir.make_dir_recursive(save_dir)
capture_frame() # capture first frame
$Timer.start()
func capture_frame() -> void:
current_frame_no += 1
if current_frame_no != skip_amount:
return
current_frame_no = 0
var image := Image.new()
if mode == Mode.PIXELORAMA:
image = get_tree().root.get_viewport().get_texture().get_data()
image.flip_y()
else:
var frame = project.frames[project.current_frame]
image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
Export.blend_selected_cels(image, frame, Vector2(0, 0), project)
if mode == Mode.CANVAS:
if resize != 100:
image.unlock()
image.resize(image.get_size().x * resize / 100, image.get_size().y * resize / 100, 0)
cache.append(image)
func _on_Timer_timeout() -> void:
# Saves frames little by little During recording
if cache.size() > 0:
save_frame(cache[0])
cache.remove(0)
func save_frame(img: Image) -> void:
var save_file = str(project.name, "_", frame_captured, ".png")
img.save_png(save_dir.plus_file(save_file))
emit_signal("frame_saved")
func _on_frame_saved():
frame_captured += 1
$ScrollContainer/CenterContainer/GridContainer/Captured.text = str("Saved: ", frame_captured)
func finalize_recording():
$Timer.stop()
for img in cache:
save_frame(img)
cache.clear()
disconnect_undo()
folder_button.visible = false
project_list.visible = true
$ScrollContainer/CenterContainer/GridContainer/Captured.visible = false
for child in $Dialogs/Options/PanelContainer/VBoxContainer.get_children():
child.visible = true
if mode == Mode.PIXELORAMA:
size.get_parent().visible = false
func disconnect_undo() -> void:
project.undo_redo.disconnect("version_changed", self, "capture_frame")
func connect_undo() -> void:
project.undo_redo.connect("version_changed", self, "capture_frame")
func _on_TargetProjectOption_item_selected(index: int) -> void:
project = Global.projects[index]
func _on_TargetProjectOption_pressed() -> void:
refresh_projects_list()
func refresh_projects_list() -> void:
project_list.clear()
for proj in Global.projects:
project_list.add_item(proj.name)
func _on_Start_toggled(button_pressed: bool) -> void:
if button_pressed:
initialize_recording()
Global.change_button_texturerect(start_button.get_child(0), "stop.png")
else:
finalize_recording()
Global.change_button_texturerect(start_button.get_child(0), "start.png")
func _on_Settings_pressed():
var settings = $Dialogs/Options
var pos = rect_position
settings.popup(Rect2(pos, settings.rect_size))
func _on_SkipAmount_value_changed(value: float) -> void:
skip_amount = value
func _on_Mode_toggled(button_pressed) -> void:
if button_pressed:
mode = Mode.PIXELORAMA
size.get_parent().visible = false
else:
mode = Mode.CANVAS
size.get_parent().visible = true
func _on_SpinBox_value_changed(value: float) -> void:
resize = value
var new_size: Vector2 = project.size * (resize / 100.0)
size.text = str("(", new_size.x, "×", new_size.y, ")")
func _on_Choose_pressed() -> void:
$Dialogs/Path.popup_centered()
$Dialogs/Path.current_dir = chosen_dir
func _on_Open_pressed() -> void:
OS.shell_open(path_field.text)
func _on_Path_dir_selected(dir: String) -> void:
chosen_dir = dir
path_field.text = chosen_dir
start_button.disabled = false
func _on_Fps_value_changed(value: float) -> void:
var dur_label = $Dialogs/Options/PanelContainer/VBoxContainer/Fps/Duration
var duration = stepify(1.0 / value, 0.0001)
dur_label.text = str("= ", duration, " sec")

View file

@ -0,0 +1,373 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://assets/graphics/recorder/start.png" type="Texture" id=1]
[ext_resource path="res://src/UI/Recorder/Recorder.gd" type="Script" id=2]
[ext_resource path="res://assets/graphics/timeline/expandable.png" type="Texture" id=3]
[ext_resource path="res://addons/keychain/assets/folder.svg" type="Texture" id=4]
[node name="Recorder" type="PanelContainer"]
margin_left = 1.0
margin_right = 195.0
margin_bottom = 50.0
rect_min_size = Vector2( 0, 36 )
script = ExtResource( 2 )
[node name="ScrollContainer" type="ScrollContainer" parent="."]
margin_left = 7.0
margin_top = 7.0
margin_right = 187.0
margin_bottom = 43.0
rect_min_size = Vector2( 36, 36 )
[node name="CenterContainer" type="CenterContainer" parent="ScrollContainer"]
margin_right = 180.0
margin_bottom = 36.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="GridContainer" type="GridContainer" parent="ScrollContainer/CenterContainer"]
margin_left = 4.0
margin_top = 2.0
margin_right = 176.0
margin_bottom = 34.0
size_flags_vertical = 0
columns = 4
[node name="Captured" type="Label" parent="ScrollContainer/CenterContainer/GridContainer"]
visible = false
margin_top = 9.0
margin_right = 32.0
margin_bottom = 23.0
[node name="TargetProjectOption" type="OptionButton" parent="ScrollContainer/CenterContainer/GridContainer"]
unique_name_in_owner = true
margin_right = 100.0
margin_bottom = 32.0
rect_min_size = Vector2( 100, 0 )
hint_tooltip = "Choose project"
clip_text = true
[node name="Start" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
unique_name_in_owner = true
margin_left = 104.0
margin_right = 136.0
margin_bottom = 32.0
rect_min_size = Vector2( 32, 32 )
hint_tooltip = "Capture .png frames"
mouse_default_cursor_shape = 2
toggle_mode = true
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/Start"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -10.0
margin_top = -10.5
margin_right = 10.0
margin_bottom = 10.5
texture = ExtResource( 1 )
expand = true
stretch_mode = 6
[node name="Settings" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
unique_name_in_owner = true
margin_left = 140.0
margin_right = 172.0
margin_bottom = 32.0
rect_min_size = Vector2( 32, 32 )
hint_tooltip = "Settings"
mouse_default_cursor_shape = 2
toggle_mode = true
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/Settings"]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -10.0
margin_top = -10.5
margin_right = 10.0
margin_bottom = 10.5
texture = ExtResource( 3 )
stretch_mode = 6
[node name="Folder" type="Button" parent="ScrollContainer/CenterContainer/GridContainer"]
unique_name_in_owner = true
visible = false
margin_left = 176.0
margin_right = 208.0
margin_bottom = 32.0
rect_min_size = Vector2( 32, 32 )
hint_tooltip = "Open Folder"
mouse_default_cursor_shape = 2
toggle_mode = true
__meta__ = {
"_editor_description_": ""
}
[node name="TextureRect" type="TextureRect" parent="ScrollContainer/CenterContainer/GridContainer/Folder"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 3.0
margin_top = 3.0
margin_right = -3.0
margin_bottom = -3.0
texture = ExtResource( 4 )
stretch_mode = 6
[node name="Dialogs" type="Control" parent="."]
margin_left = 7.0
margin_top = 7.0
margin_right = 187.0
margin_bottom = 43.0
mouse_filter = 2
[node name="Options" type="WindowDialog" parent="Dialogs"]
margin_right = 293.0
margin_bottom = 259.0
rect_min_size = Vector2( 295, 260 )
window_title = "Options"
[node name="PanelContainer" type="PanelContainer" parent="Dialogs/Options"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 9.0
margin_top = 9.0
margin_right = -9.0
margin_bottom = -9.0
[node name="VBoxContainer" type="VBoxContainer" parent="Dialogs/Options/PanelContainer"]
margin_left = 7.0
margin_top = 7.0
margin_right = 270.0
margin_bottom = 235.0
[node name="IntervalHeader" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer"]
margin_right = 263.0
margin_bottom = 14.0
custom_constants/separation = 0
[node name="Label" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/IntervalHeader"]
margin_right = 49.0
margin_bottom = 14.0
theme_type_variation = "Header"
text = "Interval"
[node name="HSeparator" type="HSeparator" parent="Dialogs/Options/PanelContainer/VBoxContainer/IntervalHeader"]
margin_left = 49.0
margin_right = 263.0
margin_bottom = 14.0
size_flags_horizontal = 3
[node name="ActionGap" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer"]
margin_top = 18.0
margin_right = 263.0
margin_bottom = 42.0
alignment = 1
[node name="Label" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/ActionGap"]
margin_left = 2.0
margin_top = 5.0
margin_right = 131.0
margin_bottom = 19.0
text = "Capture frame every"
[node name="SkipAmount" type="SpinBox" parent="Dialogs/Options/PanelContainer/VBoxContainer/ActionGap"]
margin_left = 135.0
margin_right = 209.0
margin_bottom = 24.0
[node name="Label2" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/ActionGap"]
margin_left = 213.0
margin_top = 5.0
margin_right = 261.0
margin_bottom = 19.0
text = "Actions"
[node name="Fps" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer"]
margin_top = 46.0
margin_right = 263.0
margin_bottom = 70.0
alignment = 1
[node name="Label" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/Fps"]
margin_left = 36.0
margin_top = 5.0
margin_right = 62.0
margin_bottom = 19.0
text = "Fps:"
[node name="Fps" type="SpinBox" parent="Dialogs/Options/PanelContainer/VBoxContainer/Fps"]
margin_left = 66.0
margin_right = 140.0
margin_bottom = 24.0
min_value = 1.0
value = 30.0
allow_greater = true
[node name="Duration" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/Fps"]
margin_left = 144.0
margin_top = 5.0
margin_right = 226.0
margin_bottom = 19.0
text = "= 0.0333 sec"
[node name="ModeHeader" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer" groups=["visible during recording"]]
margin_top = 74.0
margin_right = 263.0
margin_bottom = 88.0
custom_constants/separation = 0
[node name="Label" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/ModeHeader"]
margin_right = 36.0
margin_bottom = 14.0
theme_type_variation = "Header"
text = "Mode"
[node name="HSeparator" type="HSeparator" parent="Dialogs/Options/PanelContainer/VBoxContainer/ModeHeader"]
margin_left = 36.0
margin_right = 263.0
margin_bottom = 14.0
size_flags_horizontal = 3
[node name="ModeType" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer" groups=["visible during recording"]]
margin_top = 92.0
margin_right = 263.0
margin_bottom = 132.0
alignment = 1
[node name="Label" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/ModeType"]
margin_left = 15.0
margin_top = 13.0
margin_right = 93.0
margin_bottom = 27.0
text = "Canvas Only"
[node name="Mode" type="CheckButton" parent="Dialogs/Options/PanelContainer/VBoxContainer/ModeType"]
margin_left = 97.0
margin_right = 173.0
margin_bottom = 40.0
align = 1
[node name="Label2" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/ModeType"]
margin_left = 177.0
margin_top = 13.0
margin_right = 248.0
margin_bottom = 27.0
text = "Pixelorama"
[node name="ExportDimentions" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer"]
margin_top = 136.0
margin_right = 263.0
margin_bottom = 160.0
alignment = 1
[node name="Label" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/ExportDimentions"]
margin_left = 47.0
margin_top = 5.0
margin_right = 133.0
margin_bottom = 19.0
text = "Optput Scale:"
align = 1
[node name="Size" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/ExportDimentions"]
unique_name_in_owner = true
margin_left = 137.0
margin_top = 5.0
margin_right = 137.0
margin_bottom = 19.0
[node name="Resize" type="SpinBox" parent="Dialogs/Options/PanelContainer/VBoxContainer/ExportDimentions"]
margin_left = 141.0
margin_right = 215.0
margin_bottom = 24.0
mouse_default_cursor_shape = 2
min_value = 50.0
max_value = 1000.0
step = 100.0
value = 100.0
allow_greater = true
suffix = "%"
__meta__ = {
"_editor_description_": ""
}
[node name="PathHeader" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer"]
margin_top = 164.0
margin_right = 263.0
margin_bottom = 178.0
custom_constants/separation = 0
[node name="Label" type="Label" parent="Dialogs/Options/PanelContainer/VBoxContainer/PathHeader"]
margin_right = 28.0
margin_bottom = 14.0
theme_type_variation = "Header"
text = "Path"
[node name="HSeparator" type="HSeparator" parent="Dialogs/Options/PanelContainer/VBoxContainer/PathHeader"]
margin_left = 28.0
margin_right = 263.0
margin_bottom = 14.0
size_flags_horizontal = 3
[node name="PathContainer" type="HBoxContainer" parent="Dialogs/Options/PanelContainer/VBoxContainer"]
margin_top = 182.0
margin_right = 263.0
margin_bottom = 207.0
[node name="Path" type="LineEdit" parent="Dialogs/Options/PanelContainer/VBoxContainer/PathContainer"]
unique_name_in_owner = true
margin_right = 171.0
margin_bottom = 25.0
size_flags_horizontal = 3
align = 1
editable = false
placeholder_text = "Choose Destination --->"
[node name="Open" type="Button" parent="Dialogs/Options/PanelContainer/VBoxContainer/PathContainer"]
margin_left = 175.0
margin_right = 200.0
margin_bottom = 25.0
rect_min_size = Vector2( 25, 25 )
[node name="TextureRect" type="TextureRect" parent="Dialogs/Options/PanelContainer/VBoxContainer/PathContainer/Open"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 2.0
margin_top = 2.0
margin_right = -2.0
margin_bottom = -2.0
texture = ExtResource( 4 )
stretch_mode = 6
[node name="Choose" type="Button" parent="Dialogs/Options/PanelContainer/VBoxContainer/PathContainer"]
margin_left = 204.0
margin_right = 263.0
margin_bottom = 25.0
text = "Choose"
[node name="Path" type="FileDialog" parent="Dialogs"]
margin_right = 445.0
margin_bottom = 290.0
rect_min_size = Vector2( 315, 290 )
window_title = "Open a Directory"
resizable = true
mode = 2
access = 2
[node name="Timer" type="Timer" parent="."]
[connection signal="item_selected" from="ScrollContainer/CenterContainer/GridContainer/TargetProjectOption" to="." method="_on_TargetProjectOption_item_selected"]
[connection signal="pressed" from="ScrollContainer/CenterContainer/GridContainer/TargetProjectOption" to="." method="_on_TargetProjectOption_pressed"]
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/Start" to="." method="_on_Start_toggled"]
[connection signal="pressed" from="ScrollContainer/CenterContainer/GridContainer/Settings" to="." method="_on_Settings_pressed"]
[connection signal="pressed" from="ScrollContainer/CenterContainer/GridContainer/Folder" to="." method="_on_Open_pressed"]
[connection signal="value_changed" from="Dialogs/Options/PanelContainer/VBoxContainer/ActionGap/SkipAmount" to="." method="_on_SkipAmount_value_changed"]
[connection signal="value_changed" from="Dialogs/Options/PanelContainer/VBoxContainer/Fps/Fps" to="." method="_on_Fps_value_changed"]
[connection signal="toggled" from="Dialogs/Options/PanelContainer/VBoxContainer/ModeType/Mode" to="." method="_on_Mode_toggled"]
[connection signal="value_changed" from="Dialogs/Options/PanelContainer/VBoxContainer/ExportDimentions/Resize" to="." method="_on_SpinBox_value_changed"]
[connection signal="pressed" from="Dialogs/Options/PanelContainer/VBoxContainer/PathContainer/Open" to="." method="_on_Open_pressed"]
[connection signal="pressed" from="Dialogs/Options/PanelContainer/VBoxContainer/PathContainer/Choose" to="." method="_on_Choose_pressed"]
[connection signal="dir_selected" from="Dialogs/Path" to="." method="_on_Path_dir_selected"]
[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"]

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=49 format=2]
[gd_scene load_steps=50 format=2]
[ext_resource path="res://src/UI/Tools/Tools.tscn" type="PackedScene" id=1]
[ext_resource path="res://src/UI/Canvas/CanvasPreview.tscn" type="PackedScene" id=2]
@ -12,6 +12,7 @@
[ext_resource path="res://src/UI/GlobalToolOptions/GlobalToolOptions.tscn" type="PackedScene" id=10]
[ext_resource path="res://src/UI/ReferenceImages/ReferencesPanel.tscn" type="PackedScene" id=11]
[ext_resource path="res://src/UI/PerspectiveEditor/PerspectiveEditor.tscn" type="PackedScene" id=12]
[ext_resource path="res://src/UI/Recorder/Recorder.tscn" type="PackedScene" id=13]
[ext_resource path="res://addons/dockable_container/layout.gd" type="Script" id=14]
[ext_resource path="res://src/UI/CanvasPreviewContainer/CanvasPreviewContainer.tscn" type="PackedScene" id=16]
[ext_resource path="res://src/UI/ColorPickers/ColorPickers.tscn" type="PackedScene" id=17]
@ -35,7 +36,7 @@ shader_param/size = Vector2( 100, 100 )
[sub_resource type="Resource" id=1]
resource_name = "Tabs"
script = ExtResource( 36 )
names = PoolStringArray( "Tools", "Perspective Editor" )
names = PoolStringArray( "Tools", "Perspective Editor", "Recorder" )
current_tab = 0
[sub_resource type="Resource" id=8]
@ -402,14 +403,20 @@ margin_bottom = 716.0
[node name="Reference Images" parent="DockableContainer" instance=ExtResource( 11 )]
visible = false
margin_left = 1079.0
margin_right = 1080.0
margin_bottom = 1004.0
margin_right = -80.0
margin_bottom = -684.0
[node name="Perspective Editor" parent="DockableContainer" instance=ExtResource( 12 )]
visible = false
margin_right = -1010.0
margin_bottom = -710.0
[node name="Recorder" parent="DockableContainer" instance=ExtResource( 13 )]
visible = false
margin_left = 0.0
margin_right = 14.0
margin_bottom = 36.0
[connection signal="item_rect_changed" from="DockableContainer/Main Canvas" to="." method="_on_main_canvas_item_rect_changed"]
[connection signal="visibility_changed" from="DockableContainer/Main Canvas" to="." method="_on_main_canvas_visibility_changed"]
[connection signal="reposition_active_tab_request" from="DockableContainer/Main Canvas/TabsContainer/Tabs" to="DockableContainer/Main Canvas/TabsContainer/Tabs" method="_on_Tabs_reposition_active_tab_request"]