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