multi-tabs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
class_name TabManager
|
||||
extends HFlowContainer
|
||||
extends HBoxContainer
|
||||
|
||||
var tabs: Array[Tab] = []
|
||||
var active_tab := 0
|
||||
@@ -19,13 +19,38 @@ const TAB_GRADIENT_DEFAULT: GradientTexture2D = preload("res://Scenes/Styles/Tab
|
||||
|
||||
@onready var h_box_container: HBoxContainer = $HBoxContainer
|
||||
|
||||
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
|
||||
|
||||
func _ready() -> void:
|
||||
tabs.assign(get_tree().get_nodes_in_group("tabs"))
|
||||
|
||||
call_deferred("_initialize_tab_containers")
|
||||
|
||||
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))
|
||||
|
||||
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)
|
||||
|
||||
func _tab_pressed(index: int) -> void:
|
||||
set_active_tab(index)
|
||||
@@ -56,34 +81,150 @@ func _tab_closed(index: int) -> void:
|
||||
tabs[i].tab_closed.connect(_tab_closed.bind(i))
|
||||
|
||||
set_active_tab(active_tab)
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
tabs[index].show_content()
|
||||
|
||||
if not tabs[index].website_container:
|
||||
if main:
|
||||
trigger_init_scene(tabs[index])
|
||||
|
||||
active_tab = index
|
||||
|
||||
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()
|
||||
|
||||
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))
|
||||
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)
|
||||
|
||||
h_box_container.add_child(tab)
|
||||
tab.play_appear_animation(tab_width)
|
||||
|
||||
set_active_tab(index)
|
||||
|
||||
await get_tree().process_frame
|
||||
update_tab_widths()
|
||||
|
||||
trigger_init_scene(tab)
|
||||
|
||||
# WARNING: temporary
|
||||
main.render()
|
||||
|
||||
@@ -98,6 +239,9 @@ func _input(_event: InputEvent) -> void:
|
||||
if Input.is_action_just_pressed("PreviousTab"):
|
||||
var prev_tab = (active_tab - 1 + tabs.size()) % tabs.size()
|
||||
set_active_tab(prev_tab - 1)
|
||||
if Input.is_action_just_pressed("FocusSearch"):
|
||||
main.search_bar.grab_focus()
|
||||
main.search_bar.select_all()
|
||||
|
||||
func _on_new_tab_button_pressed() -> void:
|
||||
create_tab()
|
||||
|
||||
Reference in New Issue
Block a user