Files
leonwww/flumi/Scripts/Browser/TabContainer.gd

255 lines
8.1 KiB
GDScript3
Raw Normal View History

class_name TabManager
2025-08-29 16:24:53 +03:00
extends HBoxContainer
2025-07-21 19:31:07 +03:00
var tabs: Array[Tab] = []
var active_tab := 0
@onready var main: Main = $"../.."
2025-07-21 19:31:07 +03:00
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
2025-08-29 16:24:53 +03:00
const MIN_TAB_WIDTH = 24 # Minimum width (icon only)
const MIN_TAB_WIDTH_WITH_CLOSE = 60 # Minimum width when close button is visible
const MAX_TAB_WIDTH = 320 # Max width
const CLOSE_BUTTON_HIDE_THRESHOLD = 100 # When to hide close button
const TEXT_HIDE_THRESHOLD = 50 # When to hide text
const POPUP_BUTTON_WIDTH = 50 # Width of + button
const NEW_TAB_BUTTON_WIDTH = 50 # Width of new tab button
const OTHER_UI_PADDING = 200 # Space for other UI elements
2025-07-21 19:31:07 +03:00
func _ready() -> void:
tabs.assign(get_tree().get_nodes_in_group("tabs"))
2025-08-29 16:24:53 +03:00
call_deferred("_initialize_tab_containers")
2025-07-21 19:31:07 +03:00
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))
2025-08-29 16:24:53 +03:00
get_viewport().size_changed.connect(_on_viewport_resized)
call_deferred("update_tab_widths")
call_deferred("_delayed_update")
func _initialize_tab_containers() -> void:
for tab in tabs:
trigger_init_scene(tab)
func trigger_init_scene(tab: Tab) -> void:
var main_vbox = main.get_node("VBoxContainer")
tab.init_scene(main_vbox)
2025-07-21 19:31:07 +03:00
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)
2025-08-29 16:24:53 +03:00
update_tab_widths()
func _on_viewport_resized() -> void:
update_tab_widths()
func _delayed_update() -> void:
update_tab_widths()
func update_tab_widths() -> void:
if tabs.is_empty():
return
var viewport_width = get_viewport().get_visible_rect().size.x
var available_width = viewport_width - POPUP_BUTTON_WIDTH - NEW_TAB_BUTTON_WIDTH - OTHER_UI_PADDING
var tab_width = available_width / float(tabs.size())
tab_width = clamp(tab_width, MIN_TAB_WIDTH, MAX_TAB_WIDTH)
var should_hide_close = tab_width < CLOSE_BUTTON_HIDE_THRESHOLD
var should_hide_text = tab_width < TEXT_HIDE_THRESHOLD
h_box_container.custom_minimum_size.x = 0
h_box_container.size.x = 0
for tab in tabs:
if tab.appear_tween and tab.appear_tween.is_valid():
continue
tab.custom_minimum_size.x = tab_width
tab.size.x = tab_width
tab.button.custom_minimum_size.x = tab_width
tab.button.size.x = tab_width
tab.close_button.visible = not should_hide_close
tab.button.text = "" if should_hide_text else tab.button.get_meta("original_text", tab.button.text)
if not tab.button.has_meta("original_text"):
tab.button.set_meta("original_text", tab.button.text)
update_tab_internal_elements(tab, tab_width, should_hide_close, should_hide_text)
func calculate_visible_tab_count(available_width: float) -> int:
var all_tabs_width = calculate_tab_width(available_width, tabs.size())
if all_tabs_width >= MIN_TAB_WIDTH:
return tabs.size()
for tab_count in range(tabs.size(), 0, -1):
var tab_width = calculate_tab_width(available_width, tab_count)
if tab_width >= MIN_TAB_WIDTH:
return tab_count
return max(1, tabs.size())
func calculate_tab_width(available_width: float, tab_count: int) -> float:
if tab_count == 0:
return MAX_TAB_WIDTH
var ideal_width = available_width / tab_count
return clamp(ideal_width, MIN_TAB_WIDTH, MAX_TAB_WIDTH)
func get_hidden_tabs() -> Array[Tab]:
var hidden_tabs: Array[Tab] = []
for tab in tabs:
if not tab.visible:
hidden_tabs.append(tab)
return hidden_tabs
func has_hidden_tabs() -> bool:
return get_hidden_tabs().size() > 0
func update_tab_internal_elements(tab: Tab, width: float, hide_close_button: bool = false, hide_text: bool = false) -> void:
var should_show_gradient = not hide_text and not hide_close_button
tab.gradient_texture.visible = should_show_gradient
if should_show_gradient:
var gradient_start_offset = 72
var gradient_width = 64
var gradient_start_x = width - gradient_start_offset
tab.gradient_texture.position.x = gradient_start_x
tab.gradient_texture.size.x = gradient_width
if not hide_close_button:
var close_button_x = width - 34
tab.close_button.position.x = close_button_x
2025-07-21 19:31:07 +03:00
func set_active_tab(index: int) -> void:
2025-08-29 16:24:53 +03:00
if index < 0 or index >= tabs.size():
return
if active_tab >= 0 and active_tab < tabs.size():
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
if tabs[active_tab].background_panel:
tabs[active_tab].background_panel.visible = false
2025-07-21 19:31:07 +03:00
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
2025-08-29 16:24:53 +03:00
tabs[index].show_content()
if not tabs[index].website_container:
if main:
trigger_init_scene(tabs[index])
2025-07-21 19:31:07 +03:00
active_tab = index
2025-08-29 16:24:53 +03:00
if main and main.search_bar:
if tabs[index].has_content:
main.current_domain = tabs[index].current_url
var display_text = main.current_domain
if display_text.begins_with("gurt://"):
display_text = display_text.substr(7)
main.search_bar.text = display_text
else:
main.current_domain = ""
main.search_bar.text = ""
main.search_bar.grab_focus()
2025-09-06 16:58:24 +03:00
main.update_navigation_buttons()
2025-07-21 19:31:07 +03:00
func create_tab() -> void:
var index = tabs.size();
var tab = TAB.instantiate()
tabs.append(tab)
h_box_container.add_child(tab)
2025-07-21 19:31:07 +03:00
tab.tab_pressed.connect(_tab_pressed.bind(index))
tab.tab_closed.connect(_tab_closed.bind(index))
2025-08-29 16:24:53 +03:00
var viewport_width = get_viewport().get_visible_rect().size.x
var available_width = viewport_width - POPUP_BUTTON_WIDTH - NEW_TAB_BUTTON_WIDTH - OTHER_UI_PADDING
var visible_count = calculate_visible_tab_count(available_width)
var tab_width = calculate_tab_width(available_width, visible_count)
2025-07-21 19:31:07 +03:00
2025-08-29 16:24:53 +03:00
tab.play_appear_animation(tab_width)
2025-07-24 13:52:34 +03:00
2025-07-21 19:31:07 +03:00
set_active_tab(index)
2025-08-29 16:24:53 +03:00
await get_tree().process_frame
update_tab_widths()
trigger_init_scene(tab)
2025-09-07 18:49:32 +03:00
var startup_behavior = main.get_startup_behavior()
if startup_behavior.specific_page and not startup_behavior.url.is_empty():
main._on_search_submitted(startup_behavior.url, true)
else:
main.render()
2025-07-21 19:31:07 +03:00
2025-07-23 15:23:32 +03:00
func _input(_event: InputEvent) -> void:
2025-07-21 19:31:07 +03:00
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)
2025-08-29 16:24:53 +03:00
if Input.is_action_just_pressed("FocusSearch"):
main.search_bar.grab_focus()
main.search_bar.select_all()
2025-07-21 19:31:07 +03:00
func _on_new_tab_button_pressed() -> void:
create_tab()