Skip to content

Commit 1801a51

Browse files
committed
feat(Tabs): add methods for adding/removing tabs and add tab scrolling
1 parent 58aff65 commit 1801a51

File tree

7 files changed

+118
-3
lines changed

7 files changed

+118
-3
lines changed

core/systems/state/tab_container_state.gd

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,29 @@ class_name TabContainerState
99
signal tab_button_pressed(tab: int)
1010
signal tab_changed(tab: int)
1111
signal tab_selected(tab: int)
12+
signal tab_added(tab_text: String, node: ScrollContainer)
13+
signal tab_removed(tab_text: String)
1214

1315
var current_tab := 0:
1416
set(v):
1517
current_tab = v
1618
tab_changed.emit(v)
1719

1820
@export var tabs_text: PackedStringArray
21+
22+
23+
## Add the given tab
24+
func add_tab(tab_text: String, node: ScrollContainer) -> void:
25+
tabs_text.append(tab_text)
26+
tab_added.emit(tab_text, node)
27+
28+
29+
## Remove the given tab
30+
func remove_tab(tab_text: String) -> void:
31+
var idx := tabs_text.find(tab_text)
32+
if idx < 0:
33+
return
34+
tabs_text.remove_at(idx)
35+
if tabs_text.size() > current_tab:
36+
current_tab = tabs_text.size() - 1
37+
tab_removed.emit(tab_text)

core/ui/card_ui/library/library_menu.gd

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var refresh_requested := false
1717
var refresh_in_progress := false
1818
var _library := {}
1919
var _current_selection := {}
20+
var _tabs := {}
2021
var logger := Log.get_logger("LibraryMenu", Log.LEVEL.INFO)
2122

2223
@export var tabs_state: TabContainerState
@@ -40,7 +41,17 @@ func _ready() -> void:
4041
library_state.refreshed.connect(on_refresh)
4142

4243
# Listen for tab container changes
43-
tabs_state.tab_changed.connect(_on_tab_container_tab_changed)
44+
if tabs_state:
45+
var tab_nodes := tab_container.get_children()
46+
var tab_headers := tabs_state.tabs_text.duplicate()
47+
var num_tabs := mini(len(tab_nodes), tab_headers.size())
48+
for idx in range(num_tabs):
49+
var key := tab_headers[idx]
50+
var value := tab_nodes[idx]
51+
_tabs[key] = value
52+
tabs_state.tab_changed.connect(_on_tab_container_tab_changed)
53+
tabs_state.tab_added.connect(_on_tab_added)
54+
tabs_state.tab_removed.connect(_on_tab_removed)
4455

4556
# Listen for library changes
4657
var on_library_changed := func(item: LibraryItem):
@@ -273,6 +284,8 @@ func _on_tab_container_tab_changed(tab: int) -> void:
273284

274285
# Get the child container to grab focus
275286
var container: ScrollContainer = tab_container.get_child(tab)
287+
if container.get_child_count() == 0:
288+
return
276289
var grid: HFlowContainer = container.get_child(1).get_child(0)
277290

278291
# If we had a previous selection, grab focus on that.
@@ -295,3 +308,16 @@ func _on_tab_container_tab_changed(tab: int) -> void:
295308
if child.visible:
296309
child.grab_focus.call_deferred()
297310
break
311+
312+
313+
func _on_tab_added(tab_text: String, node: ScrollContainer) -> void:
314+
_tabs[tab_text] = node
315+
tab_container.add_child(node)
316+
317+
318+
func _on_tab_removed(tab_text: String) -> void:
319+
if not tab_text in _tabs:
320+
return
321+
var node := _tabs[tab_text] as Node
322+
_tabs.erase(tab_text)
323+
node.queue_free()

core/ui/card_ui/navigation/search_bar_menu.tscn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,7 @@ max_width = 34
6969
[node name="LibraryTabsContainer" parent="MarginContainer/HBoxContainer" instance=ExtResource("8_uixir")]
7070
unique_name_in_owner = true
7171
layout_mode = 2
72+
size_flags_horizontal = 3
73+
size_flags_vertical = 3
7274
tabs_state = ExtResource("9_dlgkq")
7375
show_left_separator = true
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@tool
2+
extends ScrollContainer
3+
class_name EnhancedScrollContainer
4+
5+
@export var maximum_size: Vector2i:
6+
set(v):
7+
maximum_size = v
8+
_on_sort_children()
9+
10+
func _ready() -> void:
11+
sort_children.connect(_on_sort_children)
12+
13+
14+
func _on_sort_children() -> void:
15+
if get_child_count() == 0:
16+
return
17+
var child: Control
18+
for c in get_children():
19+
if not c is Control:
20+
continue
21+
child = c
22+
break
23+
if not child:
24+
return
25+
26+
custom_minimum_size.x = min(child.size.x, maximum_size.x)
27+
custom_minimum_size.y = min(child.size.y, maximum_size.y)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://b6useuxusuw4w

core/ui/components/tabs_header.gd

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var current_tab: TabLabel
1212
@onready var l_sep := $%LSeparator
1313
@onready var r_sep := $%RSeparator
1414
@onready var container := $%TabLabelContainer
15+
@onready var scroll_container := $%EnhancedScrollContainer as EnhancedScrollContainer
1516

1617

1718
# Called when the node enters the scene tree for the first time.
@@ -38,13 +39,38 @@ func _ready() -> void:
3839

3940
# Listen for tab changes
4041
tabs_state.tab_changed.connect(_on_tab_changed)
42+
tabs_state.tab_added.connect(_on_tab_added)
43+
tabs_state.tab_removed.connect(_on_tab_removed)
4144

4245

4346
func _on_tab_changed(tab: int) -> void:
4447
for tab_label in container.get_children():
4548
tab_label.selected = false
4649
current_tab = container.get_child(tab)
4750
current_tab.selected = true
51+
if not current_tab:
52+
return
53+
var target_position := current_tab.position.x - (current_tab.size.x / 2)
54+
if target_position < 0:
55+
target_position = 0
56+
var tween := get_tree().create_tween()
57+
tween.tween_property(scroll_container, "scroll_horizontal", target_position, 0.2)
58+
59+
60+
func _on_tab_added(tab_text: String, _node: ScrollContainer) -> void:
61+
var tab := tab_label_scene.instantiate()
62+
tab.text = tab_text
63+
container.add_child(tab)
64+
65+
66+
func _on_tab_removed(tab_text: String) -> void:
67+
for child in container.get_children():
68+
if child.get("text") == null:
69+
continue
70+
if child.text != tab_text:
71+
continue
72+
child.queue_free()
73+
break
4874

4975

5076
func _input(event: InputEvent) -> void:

core/ui/components/tabs_header.tscn

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
[gd_scene load_steps=4 format=3 uid="uid://cgmb4kr2ec4ha"]
1+
[gd_scene load_steps=5 format=3 uid="uid://cgmb4kr2ec4ha"]
22

33
[ext_resource type="Script" uid="uid://cnnkmn8jnjf2m" path="res://core/ui/components/tabs_header.gd" id="1_su51c"]
44
[ext_resource type="Script" uid="uid://boeu2ttk342x8" path="res://core/ui/components/input_icon.gd" id="2_oxuw3"]
5+
[ext_resource type="Script" uid="uid://b6useuxusuw4w" path="res://core/ui/components/containers/enhanced_scroll_container.gd" id="3_2l5lr"]
56

67
[sub_resource type="StyleBoxLine" id="StyleBoxLine_lb7pt"]
78
content_margin_top = 20.0
@@ -26,7 +27,20 @@ layout_mode = 2
2627
script = ExtResource("2_oxuw3")
2728
path = "ogui_tab_left"
2829

29-
[node name="TabLabelContainer" type="HBoxContainer" parent="."]
30+
[node name="MarginContainer" type="MarginContainer" parent="."]
31+
layout_mode = 2
32+
theme_override_constants/margin_top = 5
33+
theme_override_constants/margin_bottom = 5
34+
35+
[node name="EnhancedScrollContainer" type="ScrollContainer" parent="MarginContainer"]
36+
unique_name_in_owner = true
37+
layout_mode = 2
38+
vertical_scroll_mode = 0
39+
script = ExtResource("3_2l5lr")
40+
maximum_size = Vector2i(550, 0)
41+
metadata/_custom_type_script = "uid://b6useuxusuw4w"
42+
43+
[node name="TabLabelContainer" type="HBoxContainer" parent="MarginContainer/EnhancedScrollContainer"]
3044
unique_name_in_owner = true
3145
layout_mode = 2
3246
theme_override_constants/separation = 30

0 commit comments

Comments
 (0)