tab management

This commit is contained in:
Face
2025-07-21 19:31:07 +03:00
parent 438beb01dc
commit b3526927cb
22 changed files with 475 additions and 82 deletions

6
Scripts/Constants.gd Normal file
View File

@@ -0,0 +1,6 @@
extends Node
const MAIN_COLOR = Color(27/255.0, 27/255.0, 27/255.0, 1)
const SECONDARY_COLOR = Color(43/255.0, 43/255.0, 43/255.0, 1)
const HOVER_COLOR = Color(0, 0, 0, 1)

1
Scripts/Constants.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://b4qt7eno8g41p

65
Scripts/Tab.gd Normal file
View File

@@ -0,0 +1,65 @@
class_name Tab
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
const TAB_GRADIENT: GradientTexture2D = preload("res://Scenes/Styles/TabGradient.tres")
const TAB_GRADIENT_DEFAULT: GradientTexture2D = preload("res://Scenes/Styles/TabGradientDefault.tres")
const TAB_GRADIENT_INACTIVE: GradientTexture2D = preload("res://Scenes/Styles/TabGradientInactive.tres")
const TAB_HOVER: StyleBoxFlat = preload("res://Scenes/Styles/TabHover.tres")
const TAB_DEFAULT: StyleBoxFlat = preload("res://Scenes/Styles/TabDefault.tres")
const CLOSE_BUTTON_HOVER: StyleBoxFlat = preload("res://Scenes/Styles/CloseButtonHover.tres")
const CLOSE_BUTTON_NORMAL: StyleBoxFlat = preload("res://Scenes/Styles/CloseButtonNormal.tres")
var is_active := false
var mouse_over_tab := false
func _ready():
add_to_group("tabs")
gradient_texture.texture = gradient_texture.texture.duplicate()
gradient_texture.texture.gradient = gradient_texture.texture.gradient.duplicate()
func _process(_delta):
# NOTE: probably very inefficient
if mouse_over_tab:
var mouse_pos = get_global_mouse_position()
var close_button_rect = Rect2(close_button.global_position, close_button.size * close_button.scale)
if close_button_rect.has_point(mouse_pos):
close_button.add_theme_stylebox_override("normal", CLOSE_BUTTON_HOVER)
else:
close_button.add_theme_stylebox_override("normal", CLOSE_BUTTON_NORMAL)
func _on_button_mouse_entered() -> void:
mouse_over_tab = true
if is_active: return
gradient_texture.texture = TAB_GRADIENT_INACTIVE
func _on_button_mouse_exited() -> void:
mouse_over_tab = false
if is_active: return
gradient_texture.texture = TAB_GRADIENT_DEFAULT
func _exit_tree():
remove_from_group("tabs")
func _on_button_pressed() -> void:
# Check if click was on close button area
var mouse_pos = get_global_mouse_position()
var close_button_rect = Rect2(close_button.global_position, close_button.size * close_button.scale)
if close_button_rect.has_point(mouse_pos):
_on_close_button_pressed()
else:
# Handle tab button click
tab_pressed.emit()
func _on_close_button_pressed() -> void:
tab_closed.emit()
queue_free()

1
Scripts/Tab.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://crpnnfqm3k5xv

96
Scripts/TabContainer.gd Normal file
View File

@@ -0,0 +1,96 @@
extends HFlowContainer
var tabs: Array[Tab] = []
var active_tab := 0
const TAB = preload("res://Scenes/Tab.tscn")
const TAB_NORMAL: StyleBoxFlat = preload("res://Scenes/Styles/TabNormal.tres")
const TAB_HOVER: StyleBoxFlat = preload("res://Scenes/Styles/TabHover.tres")
const TAB_DEFAULT: StyleBoxFlat = preload("res://Scenes/Styles/TabDefault.tres")
const TAB_HOVER_DEFAULT: StyleBoxFlat = preload("res://Scenes/Styles/TabHoverDefault.tres")
const TAB_GRADIENT: GradientTexture2D = preload("res://Scenes/Styles/TabGradient.tres")
const TAB_GRADIENT_DEFAULT: GradientTexture2D = preload("res://Scenes/Styles/TabGradientDefault.tres")
@onready var h_box_container: HBoxContainer = $HBoxContainer
func _ready() -> void:
tabs.assign(get_tree().get_nodes_in_group("tabs"))
set_active_tab(0)
for i in tabs.size():
tabs[i].tab_pressed.connect(_tab_pressed.bind(i))
tabs[i].tab_closed.connect(_tab_closed.bind(i))
func _tab_pressed(index: int) -> void:
set_active_tab(index)
func _tab_closed(index: int) -> void:
tabs.remove_at(index)
if tabs.is_empty():
get_tree().quit()
return
if index <= active_tab:
if index == active_tab:
# Closed tab was active, select right neighbor (or last tab if at end)
if index >= tabs.size():
active_tab = tabs.size() - 1
else:
active_tab = index
else:
# Closed tab was before active tab, shift active index down
active_tab -= 1
# Reconnect signals with updated indices
for i in tabs.size():
tabs[i].tab_pressed.disconnect(_tab_pressed)
tabs[i].tab_closed.disconnect(_tab_closed)
tabs[i].tab_pressed.connect(_tab_pressed.bind(i))
tabs[i].tab_closed.connect(_tab_closed.bind(i))
set_active_tab(active_tab)
func set_active_tab(index: int) -> void:
# old tab
tabs[active_tab].is_active = false
tabs[active_tab].button.add_theme_stylebox_override("normal", TAB_DEFAULT)
tabs[active_tab].button.add_theme_stylebox_override("pressed", TAB_DEFAULT)
tabs[active_tab].button.add_theme_stylebox_override("hover", TAB_HOVER_DEFAULT)
tabs[active_tab].gradient_texture.texture = TAB_GRADIENT_DEFAULT
# new tab
tabs[index].is_active = true
tabs[index].button.add_theme_stylebox_override("normal", TAB_NORMAL)
tabs[index].button.add_theme_stylebox_override("pressed", TAB_NORMAL)
tabs[index].button.add_theme_stylebox_override("hover", TAB_NORMAL)
tabs[index].gradient_texture.texture = TAB_GRADIENT
active_tab = index
func create_tab() -> void:
var index = tabs.size();
var tab = TAB.instantiate()
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)
set_active_tab(index)
func _input(event: InputEvent) -> void:
if Input.is_action_just_pressed("NewTab"):
create_tab()
if Input.is_action_just_pressed("CloseTab"):
tabs[active_tab]._on_close_button_pressed()
if Input.is_action_just_pressed("NextTab"):
var next_tab = (active_tab + 1) % tabs.size()
set_active_tab(next_tab)
if Input.is_action_just_pressed("PreviousTab"):
var prev_tab = (active_tab - 1 + tabs.size()) % tabs.size()
set_active_tab(prev_tab - 1)
func _on_new_tab_button_pressed() -> void:
create_tab()

View File

@@ -0,0 +1 @@
uid://cy0c74thgjwok