diff --git a/addons/script-ide/icon/pin.svg b/addons/script-ide/icon/pin.svg new file mode 100644 index 0000000..cc1be70 --- /dev/null +++ b/addons/script-ide/icon/pin.svg @@ -0,0 +1 @@ + diff --git a/addons/script-ide/icon/pin.svg.import b/addons/script-ide/icon/pin.svg.import new file mode 100644 index 0000000..4653360 --- /dev/null +++ b/addons/script-ide/icon/pin.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://khcndpjwfvkc" +path="res://.godot/imported/pin.svg-7a38377e68dd470181b224010de04380.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/script-ide/icon/pin.svg" +dest_files=["res://.godot/imported/pin.svg-7a38377e68dd470181b224010de04380.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 diff --git a/addons/script-ide/icon/unpin.svg b/addons/script-ide/icon/unpin.svg new file mode 100644 index 0000000..3d5d175 --- /dev/null +++ b/addons/script-ide/icon/unpin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/script-ide/icon/unpin.svg.import b/addons/script-ide/icon/unpin.svg.import new file mode 100644 index 0000000..70346f5 --- /dev/null +++ b/addons/script-ide/icon/unpin.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://4816ua8c10kn" +path="res://.godot/imported/unpin.svg-325c68c89e8429070a58abe0dfe905c2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/script-ide/icon/unpin.svg" +dest_files=["res://.godot/imported/unpin.svg-325c68c89e8429070a58abe0dfe905c2.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 diff --git a/addons/script-ide/plugin.gd b/addons/script-ide/plugin.gd index 3a7d39f..ba26006 100644 --- a/addons/script-ide/plugin.gd +++ b/addons/script-ide/plugin.gd @@ -20,6 +20,9 @@ const INLINE: StringName = &"@" const BUILT_IN_SCRIPT: StringName = &"::GDScript" +const CustomTabBar := preload("tabbar/custom_tab_bar.gd") +const CustomTabContainer := preload("tabbar/custom_tab_container.gd") + #region Settings and Shortcuts ## Editor setting path const SCRIPT_IDE: StringName = &"plugin/script_ide/" @@ -38,6 +41,8 @@ const SCRIPT_LIST_VISIBLE: StringName = SCRIPT_IDE + &"script_list_visible" const SCRIPT_TABS_VISIBLE: StringName = SCRIPT_IDE + &"script_tabs_visible" ## Editor setting to control where the script tabs should be. const SCRIPT_TAB_POSITION_TOP: StringName = SCRIPT_IDE + &"script_tab_position_top" +## Editor setting to control multiline tabs. +const SCRIPT_MULTILINE_TABS: StringName = SCRIPT_IDE + &"script_multiline_tabs" ## Editor setting for the 'Open Outline Popup' shortcut const OPEN_OUTLINE_POPUP: StringName = SCRIPT_IDE + &"open_outline_popup" @@ -78,6 +83,7 @@ var hide_private_members: bool = false var is_auto_navigate_in_fs: bool = true var is_script_tabs_visible: bool = true var is_script_tabs_top: bool = true +var is_script_multiline_tabs: bool = false var outline_order: PackedStringArray var open_outline_popup_shc: Shortcut @@ -95,6 +101,8 @@ var scripts_tab_bar: TabBar var script_filter_txt: LineEdit var scripts_item_list: ItemList var panel_container: VSplitContainer +var custom_tab_bar: CustomTabBar +var custom_tab_container: CustomTabContainer var split_container: HSplitContainer var old_outline: ItemList @@ -167,6 +175,18 @@ func _enter_tree() -> void: # Make tab container visible. scripts_tab_container = find_or_null(script_editor.find_children("*", "TabContainer", true, false)) scripts_tab_bar = scripts_tab_container.get_tab_bar() + scripts_tab_bar.visible = false + + custom_tab_container = CustomTabContainer.new() + custom_tab_container.scripts_tab_container = scripts_tab_container + custom_tab_container.scripts_item_list = scripts_item_list + scripts_tab_container.get_parent().add_theme_constant_override(&"separation", 0) + scripts_tab_container.get_parent().add_child(custom_tab_container) + scripts_tab_container.get_parent().move_child(custom_tab_container, 0) + custom_tab_bar = custom_tab_container.custom_tab_bar + custom_tab_container.visible = is_script_tabs_visible and is_script_multiline_tabs + + scripts_tab_container.tabs_visible = false # Save old tab state to restore later. tab_state = TabStateCache.new() @@ -176,7 +196,7 @@ func _enter_tree() -> void: create_set_scripts_popup() # Configure tab container and bar. - scripts_tab_container.tabs_visible = is_script_tabs_visible + scripts_tab_container.tabs_visible = is_script_tabs_visible and not is_script_multiline_tabs scripts_tab_container.drag_to_rearrange_enabled = true scripts_tab_container.auto_translate_mode = Node.AUTO_TRANSLATE_MODE_DISABLED update_tabs_position() @@ -275,6 +295,9 @@ func _exit_tree() -> void: scripts_tab_container.pre_popup_pressed.disconnect(prepare_scripts_popup) scripts_tab_container.set_popup(null) + scripts_tab_container.get_parent().remove_child(custom_tab_container) + custom_tab_container.set_popup(null) + custom_tab_container.free() scripts_popup.free() if (scripts_tab_bar != null): @@ -381,6 +404,7 @@ func init_settings(): is_script_list_visible = get_setting(SCRIPT_LIST_VISIBLE, is_script_list_visible) is_auto_navigate_in_fs = get_setting(AUTO_NAVIGATE_IN_FS, is_auto_navigate_in_fs) is_script_tabs_visible = get_setting(SCRIPT_TABS_VISIBLE, is_script_tabs_visible) + is_script_multiline_tabs = get_setting(SCRIPT_MULTILINE_TABS, is_script_multiline_tabs) is_script_tabs_top = get_setting(SCRIPT_TAB_POSITION_TOP, is_script_tabs_top) init_outline_order() @@ -573,6 +597,9 @@ func create_set_scripts_popup(): scripts_tab_container.pre_popup_pressed.connect(prepare_scripts_popup) scripts_tab_container.set_popup(scripts_popup) + custom_tab_container.pre_popup_pressed.connect(prepare_scripts_popup) + custom_tab_container.set_popup(scripts_popup) + func prepare_scripts_popup(): scripts_popup.size.x = outline.size.x scripts_popup.size.y = panel_container.size.y - scripts_tab_bar.size.y @@ -922,13 +949,21 @@ func sync_settings(): if (new_script_tabs_visible != is_script_tabs_visible): is_script_tabs_visible = new_script_tabs_visible - scripts_tab_container.tabs_visible = is_script_tabs_visible + scripts_tab_container.tabs_visible = is_script_tabs_visible and not is_script_multiline_tabs + custom_tab_container.visible = is_script_tabs_visible and is_script_multiline_tabs elif (setting == SCRIPT_TAB_POSITION_TOP): var new_script_tabs_top: bool = get_setting(SCRIPT_TAB_POSITION_TOP, is_script_tabs_top) if (new_script_tabs_top != is_script_tabs_top): is_script_tabs_top = new_script_tabs_top update_tabs_position() + elif (setting == SCRIPT_MULTILINE_TABS): + var new_script_multiline_tabs: bool = get_setting(SCRIPT_MULTILINE_TABS, is_script_multiline_tabs) + if (new_script_multiline_tabs != is_script_multiline_tabs): + is_script_multiline_tabs = new_script_multiline_tabs + + scripts_tab_container.tabs_visible = is_script_tabs_visible and not is_script_multiline_tabs + custom_tab_container.visible = is_script_tabs_visible and is_script_multiline_tabs elif (setting == AUTO_NAVIGATE_IN_FS): is_auto_navigate_in_fs = get_setting(AUTO_NAVIGATE_IN_FS, is_auto_navigate_in_fs) elif (setting == OPEN_OUTLINE_POPUP): @@ -966,7 +1001,7 @@ func get_shortcut(property: StringName) -> Shortcut: return get_editor_settings().get_setting(property) func on_tab_changed(index: int): - selected_tab = index; + selected_tab = index if (old_script_editor_base != null): old_script_editor_base.edited_script_changed.disconnect(update_selected_tab) @@ -1302,3 +1337,21 @@ class TabStateCache: tab_bar.drag_to_rearrange_enabled = drag_to_rearrange_enabled tab_bar.tab_close_display_policy = tab_close_display_policy tab_bar.select_with_rmb = select_with_rmb + + +func _get_window_layout(configuration: ConfigFile) -> void: + var tabs: Array + for tab in custom_tab_bar.get_children(): + if tab.pinned: + tabs.append(tab.get_tab_path()) + configuration.set_value("script-ide", "pinned_tabs", tabs) + + +func _set_window_layout(configuration: ConfigFile) -> void: + var tabs: Array = configuration.get_value("script-ide", "pinned_tabs", []) + custom_tab_container.synced.connect(_update_pinned_tabs.bind(tabs), CONNECT_ONE_SHOT) + + +func _update_pinned_tabs(pinned_tabs: Array) -> void: + for tab in pinned_tabs: + custom_tab_bar.pin_tab(tab) diff --git a/addons/script-ide/tabbar/custom_tab.gd b/addons/script-ide/tabbar/custom_tab.gd new file mode 100644 index 0000000..c127c65 --- /dev/null +++ b/addons/script-ide/tabbar/custom_tab.gd @@ -0,0 +1,181 @@ +@tool +extends Button + +signal tab_pinned(idx: int, pinned: bool) +signal tab_close_pressed(idx: int) + +var container: HBoxContainer = HBoxContainer.new() +var texture_rect: TextureRect = TextureRect.new() +var label: Label = Label.new() +var pin_button: Button = Button.new() +var close_button: Button = Button.new() + +var pinned: bool = false: + set(value): + pinned = value + _update_buttons() + +var title: String: + set(value): + label.text = value + get(): + return label.text + +var tab_icon: Texture2D: + set(value): + texture_rect.texture = value + get(): + return texture_rect.texture + +var pin_icon: Texture2D: + set(value): + pin_icon = value + if pinned: pin_button.icon = value + +var unpin_icon: Texture2D: + set(value): + unpin_icon = value + if not pinned: pin_button.icon = value + +var close_icon: Texture2D: + set(value): + close_button.icon = value + +var font_unselected_color: Color +var font_hovered_color: Color +var font_selected_color: Color +var icon_color: Color: + set(value): + texture_rect.self_modulate = value + get(): + return texture_rect.self_modulate + +func _ready() -> void: + toggle_mode = true + action_mode = ACTION_MODE_BUTTON_PRESS + toggled.connect(_update_buttons.unbind(1)) + mouse_entered.connect(_update_buttons, CONNECT_DEFERRED) + mouse_exited.connect(_update_buttons, CONNECT_DEFERRED) + + var separator := Control.new() + separator.custom_minimum_size.x = 12 * EditorInterface.get_editor_scale() + separator.mouse_filter = Control.MOUSE_FILTER_PASS + container.add_child(separator) + + texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + container.add_child(texture_rect) + + label.add_theme_color_override(&"font_color", Color.WHITE) + container.add_child(label) + + pin_button.flat = true + pin_button.focus_mode = Control.FOCUS_NONE + pin_button.mouse_filter = Control.MOUSE_FILTER_PASS + pin_button.add_theme_color_override(&"icon_disabled_color", Color.TRANSPARENT) + pin_button.pressed.connect(_on_pinned_pressed) + container.add_child(pin_button) + + close_button.flat = true + close_button.focus_mode = Control.FOCUS_NONE + close_button.mouse_filter = Control.MOUSE_FILTER_PASS + close_button.add_theme_color_override(&"icon_disabled_color", Color.TRANSPARENT) + close_button.pressed.connect(_on_close_pressed) + container.add_child(close_button) + + container.add_theme_constant_override(&"separation", 0) + container.minimum_size_changed.connect(_on_container_item_rect_changed, CONNECT_DEFERRED) + add_child(container) + + _update_buttons() + + +func get_tab_path() -> String: + return tooltip_text.trim_suffix(" Class Reference") + + +func _gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_MIDDLE and event.pressed: + _on_close_pressed() + + +func _on_pinned_pressed() -> void: + pinned = not pinned + _update_buttons() + tab_pinned.emit(get_index(), pinned) + + +func _on_close_pressed() -> void: + tab_close_pressed.emit(get_index()) + + +func _update_buttons() -> void: + pin_button.disabled = not (is_hovered() or button_pressed or pinned) + close_button.disabled = not (is_hovered() or button_pressed) + pin_button.icon = pin_icon if pinned else unpin_icon + label.self_modulate = font_selected_color if button_pressed else (font_hovered_color if is_hovered() else font_unselected_color) + z_index = 0 + draw_drop_mark = false + + +func _on_container_item_rect_changed() -> void: + custom_minimum_size = container.size + + +#region Drag'n'Drop + +var drop_mark_color: Color = EditorInterface.get_editor_theme().get_color(&"drop_mark_color", &"TabBar") + +var drop_mark_width: float = 6.0 * EditorInterface.get_editor_scale() +var drop_mark_offset: float = drop_mark_width / 2.0 + +var draw_drop_mark: bool = false +var is_drop_mark_left: bool = false + +func _draw() -> void: + if draw_drop_mark: + draw_rect(Rect2(-drop_mark_offset + (0 if is_drop_mark_left else size.x), 0, drop_mark_width, size.y), drop_mark_color) + + +func _get_drag_data(at_position: Vector2) -> Variant: + var hbox := HBoxContainer.new() + hbox.z_index = 2 + var icon := TextureRect.new() + icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + icon.texture = tab_icon + icon.self_modulate = icon_color + hbox.add_child(icon) + + var label := Label.new() + label.text = title + hbox.add_child(label) + + set_drag_preview(hbox) + set_meta("__tab", true) + return self + + +func _can_drop_data(at_position: Vector2, data: Variant) -> bool: + var can_drop: bool = data.get_meta("__tab", false) + if can_drop: + is_drop_mark_left = at_position.x <= size.x / 2 + z_index = 1 + draw_drop_mark = true + queue_redraw() + return can_drop + + +func _drop_data(at_position: Vector2, data: Variant) -> void: + if data == self: + return + var is_left := at_position.x <= size.x / 2 + var from: int = data.get_index() + var to: int + if from > get_index(): + to = get_index() + (0 if is_left else 1) + else: + to = get_index() - (1 if is_left else 0) + if from != to: + get_parent().move_tab(from, to) + +#endregion diff --git a/addons/script-ide/tabbar/custom_tab_bar.gd b/addons/script-ide/tabbar/custom_tab_bar.gd new file mode 100644 index 0000000..c88b288 --- /dev/null +++ b/addons/script-ide/tabbar/custom_tab_bar.gd @@ -0,0 +1,168 @@ +@tool +extends HFlowContainer + +signal tab_changed(idx: int) +signal tab_pinned(idx: int, pinned: bool) +signal tab_close_pressed(idx: int) +signal tab_rearranged(from: int, to: int) + +const Tab := preload("custom_tab.gd") +const pin_icon: Texture2D = preload("../icon/pin.svg") +const unpin_icon: Texture2D = preload("../icon/unpin.svg") + +var style_tab_selected: StyleBoxFlat = EditorInterface.get_editor_theme().get_stylebox(&"tab_selected", &"TabBar").duplicate(true) +var style_tab_unselected: StyleBoxFlat = EditorInterface.get_editor_theme().get_stylebox(&"tab_unselected", &"TabBar").duplicate(true) +var style_tab_hovered: StyleBoxFlat = EditorInterface.get_editor_theme().get_stylebox(&"tab_hovered", &"TabBar").duplicate(true) + +var current_tab: int = -1: + set(value): + current_tab = value + if current_tab >= 0 and current_tab < get_child_count(): + get_child(current_tab).button_pressed = true + +var button_group: ButtonGroup = ButtonGroup.new() + +var scripts_item_list: ItemList +var scripts_tab_container: TabContainer + +var tabs: Dictionary + +func _ready() -> void: + add_theme_constant_override(&"h_separation", 0) + add_theme_constant_override(&"v_separation", 0) + + button_group.pressed.connect(_on_tab_changed) + + +func clear_tabs() -> void: + tabs.clear() + if get_child_count() == 0: + return + for i in range(get_child_count() - 1, -1, -1): + var child := get_child(i) + remove_child(child) + child.queue_free() + + +func get_tab(idx: int) -> Tab: + if idx >= 0 and idx < get_child_count(): + return get_child(idx) + return null + + +func get_tab_count() -> int: + return get_child_count() + + +func add_tab(title: String, icon: Texture2D = null) -> Tab: + var tab := Tab.new() + tab.title = title + tab.tab_icon = icon + + tab.button_group = button_group + + tab.pin_icon = pin_icon + tab.unpin_icon = unpin_icon + tab.close_icon = EditorInterface.get_editor_theme().get_icon(&"Close", &"EditorIcons") + + tab.add_theme_stylebox_override(&"normal", style_tab_unselected) + tab.add_theme_stylebox_override(&"pressed", style_tab_selected) + tab.add_theme_stylebox_override(&"focus", style_tab_selected) + tab.add_theme_stylebox_override(&"hover", style_tab_hovered) + tab.font_hovered_color = EditorInterface.get_editor_theme().get_color(&"font_hovered_color", &"TabBar") + tab.font_unselected_color = EditorInterface.get_editor_theme().get_color(&"font_unselected_color", &"TabBar") + tab.font_selected_color = EditorInterface.get_editor_theme().get_color(&"font_selected_color", &"TabBar") + + tab.tab_pinned.connect(_on_tab_pinned) + tab.tab_close_pressed.connect(_on_tab_close_pressed) + add_child(tab) + return tab + + +func set_tab_title(idx: int, text: String) -> void: + var tab := get_tab(idx) + if tab: + tab.title = text + + +#func get_tab_title(idx: int) -> String: + #var tab := get_tab(idx) + #if tab: + #return tab.title + #return "" + + +func pin_tab(path: String) -> void: + var tab: Tab = tabs.get(path, null) + if tab: + tab._on_pinned_pressed() + +#func get_tab_icon(idx: int) -> Texture2D: + #var tab := get_tab(idx) + #if tab: + #return tab.tab_icon + #return null + + +#func get_tab_icon_color(idx: int) -> Color: + #var tab := get_tab(idx) + #if tab: + #return tab.icon_color + #return Color.WHITE + + +func set_tab_tooltip(idx: int, text: String) -> void: + var tab := get_tab(idx) + if tab: + tab.tooltip_text = text + + +func _on_tab_close_pressed(idx: int) -> void: + tab_close_pressed.emit(idx) + var tab := get_tab(idx) + if tab: + remove_child(tab) + tab.queue_free() + + +func _on_tab_changed(button: BaseButton) -> void: + if not button: + return + var idx := button.get_index() + if current_tab == idx: + return + current_tab = idx + tab_changed.emit(idx) + + +func _on_tab_pinned(idx: int, pinned: bool) -> void: + var tab: Tab = get_tab(idx) + if pinned: + for i in range(0, get_child_count()): + var child: Tab = get_tab(i) + if child == tab: + break + if not child.pinned: + move_tab(idx, i) + break + else: + for i in range(get_child_count() - 1, -1, -1): + var child: Tab = get_tab(i) + if child == tab: + break + if child.pinned: + move_tab(idx, i) + break + + +func move_tab(from: int, to: int, with_signal: bool = true) -> void: + move_child(get_child(from), to) + if with_signal: + tab_rearranged.emit(from, to) + + var right_tab := get_tab(to + 1) + var left_tab := get_tab(to - 1) + if right_tab and right_tab.pinned: + get_tab(to).pinned = true + elif left_tab and not left_tab.pinned: + get_tab(to).pinned = false diff --git a/addons/script-ide/tabbar/custom_tab_container.gd b/addons/script-ide/tabbar/custom_tab_container.gd new file mode 100644 index 0000000..83d646d --- /dev/null +++ b/addons/script-ide/tabbar/custom_tab_container.gd @@ -0,0 +1,134 @@ +@tool +extends PanelContainer + +signal synced() +signal pre_popup_pressed() + +const CustomTabBar := preload("custom_tab_bar.gd") + +var custom_tab_bar: CustomTabBar = CustomTabBar.new() +var popup_button: Button = Button.new() +var popup_panel: PopupPanel + +var scripts_tab_container: TabContainer +var scripts_item_list: ItemList +var active_script_editor: ScriptEditorBase + +func _ready() -> void: + add_theme_stylebox_override(&"panel", EditorInterface.get_editor_theme().get_stylebox(&"tabbar_background", &"TabContainer")) + + var hsplit := HSplitContainer.new() + hsplit.add_theme_constant_override(&"separation", 0) + hsplit.dragger_visibility = SplitContainer.DRAGGER_HIDDEN_COLLAPSED + add_child(hsplit) + + custom_tab_bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL + hsplit.add_child(custom_tab_bar) + + var texture_rect: TextureRect = TextureRect.new() + texture_rect.texture = EditorInterface.get_editor_theme().get_icon(&"menu", &"TabContainer") + texture_rect.set_anchors_and_offsets_preset(Control.PRESET_CENTER) + popup_button.add_child(texture_rect) + + popup_button.focus_mode = Control.FOCUS_NONE + popup_button.custom_minimum_size = Vector2(16, 24) * EditorInterface.get_editor_scale() + popup_button.size_flags_horizontal = Control.SIZE_SHRINK_END + popup_button.pressed.connect(_on_popup_button_pressed) + hsplit.add_child(popup_button) + + scripts_tab_container.tab_changed.connect(_on_script_tab_changed) + scripts_tab_container.child_order_changed.connect(_on_script_tab_rearranged, CONNECT_DEFERRED | CONNECT_ONE_SHOT) + scripts_tab_container.child_entered_tree.connect(_queue_sync.unbind(1)) + scripts_tab_container.child_exiting_tree.connect(_queue_sync.unbind(1)) + custom_tab_bar.tab_changed.connect(_on_tab_changed) + custom_tab_bar.tab_close_pressed.connect(_on_tab_close_pressed) + custom_tab_bar.tab_rearranged.connect(_on_tab_rearranged) + + EditorInterface.get_resource_filesystem().filesystem_changed.connect(sync_tab_names) + EditorInterface.get_script_editor().editor_script_changed.connect(_on_editor_script_changed) + if active_script_editor: + active_script_editor.edited_script_changed.connect(_on_edited_script_changed) + + sync_tabs() + + +func sync_tab_names() -> void: + await get_tree().process_frame + await get_tree().process_frame + for i in scripts_tab_container.get_tab_count(): + custom_tab_bar.set_tab_title(i, scripts_tab_container.get_tab_title(i)) + + +func _on_editor_script_changed(script: Script) -> void: + if active_script_editor: + active_script_editor.edited_script_changed.disconnect(_on_edited_script_changed) + active_script_editor = EditorInterface.get_script_editor().get_current_editor() + if active_script_editor: + active_script_editor.edited_script_changed.connect(_on_edited_script_changed) + + +func _on_edited_script_changed() -> void: + var idx := scripts_tab_container.current_tab + custom_tab_bar.set_tab_title(idx, scripts_tab_container.get_tab_title(idx)) + + +var is_syncing: bool = false +func _queue_sync() -> void: + if is_syncing: + return + is_syncing = true + sync_tabs.call_deferred() + + +func sync_tabs() -> void: + custom_tab_bar.clear_tabs() + + for i in scripts_item_list.item_count: + var tab := custom_tab_bar.add_tab(scripts_item_list.get_item_text(i), scripts_item_list.get_item_icon(i)) + tab.icon_color = scripts_item_list.get_item_icon_modulate(i) + tab.tooltip_text = scripts_item_list.get_item_tooltip(i) + custom_tab_bar.tabs[tab.get_tab_path()] = tab + + custom_tab_bar.current_tab = scripts_tab_container.current_tab + + is_syncing = false + + synced.emit() + + +func sync_current_tab(idx: int, custom: bool) -> void: + if custom: + scripts_tab_container.current_tab = idx + custom_tab_bar.current_tab = idx + + +func set_popup(popup: PopupPanel) -> void: + popup_panel = popup + +func _on_script_tab_changed(idx: int) -> void: + sync_current_tab(idx, false) + + +func _on_script_tab_rearranged() -> void: + custom_tab_bar.move_tab(custom_tab_bar.current_tab, scripts_tab_container.current_tab, false) + sync_current_tab(scripts_tab_container.current_tab, true) + scripts_tab_container.child_order_changed.connect(_on_script_tab_rearranged, CONNECT_DEFERRED | CONNECT_ONE_SHOT) + + +func _on_tab_changed(idx: int) -> void: + sync_current_tab(idx, true) + + +func _on_tab_close_pressed(idx: int) -> void: + scripts_tab_container.remove_child(scripts_tab_container.get_child(idx)) + + +func _on_tab_rearranged(from: int, to: int) -> void: + scripts_tab_container.move_child(scripts_tab_container.get_child(from), to) + sync_current_tab(to, true) + + +func _on_popup_button_pressed() -> void: + pre_popup_pressed.emit() + popup_panel.position = popup_button.get_screen_position() - Vector2(popup_panel.size.x, 0) + popup_panel.popup()