chobit/shared/godot/chat/conversation_list.gd

152 lines
4.1 KiB
GDScript

extends VBoxContainer
## Collapsible conversation history list for the chat window.
## Shows recent conversations, highlights the active one, emits switch signals.
const MAX_VISIBLE: int = 10
var _item_container: VBoxContainer
var _expanded: bool = false
func setup() -> void:
visible = false
size_flags_horizontal = Control.SIZE_EXPAND_FILL
var wrapper := PanelContainer.new()
var style := StyleBoxFlat.new()
style.bg_color = UiTheme.bg_panel
style.set_border_width_all(1)
style.border_color = UiTheme.border
style.content_margin_left = 6
style.content_margin_right = 6
style.content_margin_top = 6
style.content_margin_bottom = 6
wrapper.add_theme_stylebox_override("panel", style)
add_child(wrapper)
var scroll := ScrollContainer.new()
scroll.custom_minimum_size.y = 0
scroll.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
wrapper.add_child(scroll)
_item_container = VBoxContainer.new()
_item_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_item_container.add_theme_constant_override("separation", 2)
scroll.add_child(_item_container)
EventBus.conversation_changed.connect(_on_conversation_changed)
func toggle() -> void:
if _expanded:
collapse()
else:
expand()
func expand() -> void:
_expanded = true
refresh()
visible = true
func collapse() -> void:
_expanded = false
visible = false
func refresh() -> void:
for child: Node in _item_container.get_children():
child.queue_free()
var conversations: Array[Dictionary] = _get_conversations()
var active_id: String = _get_active_id()
if conversations.is_empty():
var empty_label := Label.new()
empty_label.text = "No conversations yet"
empty_label.add_theme_color_override("font_color", UiTheme.text_muted)
empty_label.add_theme_font_size_override("font_size", 11)
_item_container.add_child(empty_label)
return
var count: int = 0
for conv: Dictionary in conversations:
if count >= MAX_VISIBLE:
break
var id: String = str(conv.get("id", ""))
var title: String = str(conv.get("title", "Untitled"))
var msg_count: int = int(conv.get("message_count", 0))
var is_active: bool = id == active_id
_item_container.add_child(_build_item(id, title, msg_count, is_active))
count += 1
func _build_item(
id: String,
title: String,
msg_count: int,
is_active: bool,
) -> Control:
var btn := Button.new()
btn.flat = true
btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
btn.alignment = HORIZONTAL_ALIGNMENT_LEFT
btn.custom_minimum_size.y = 32
var display_title := title if not title.is_empty() else "New conversation"
var suffix := " (%d)" % msg_count if msg_count > 0 else ""
btn.text = display_title + suffix
if is_active:
btn.add_theme_color_override("font_color", UiTheme.accent)
btn.add_theme_color_override("font_hover_color", UiTheme.accent)
else:
btn.add_theme_color_override("font_color", UiTheme.text_primary)
btn.add_theme_color_override("font_hover_color", UiTheme.accent)
btn.add_theme_font_size_override("font_size", 12)
var hover_style := StyleBoxFlat.new()
hover_style.bg_color = UiTheme.item_hover
hover_style.set_corner_radius_all(4)
btn.add_theme_stylebox_override("hover", hover_style)
var normal_style := StyleBoxEmpty.new()
btn.add_theme_stylebox_override("normal", normal_style)
btn.add_theme_stylebox_override("pressed", hover_style)
btn.add_theme_stylebox_override("focus", StyleBoxEmpty.new())
if not is_active:
btn.pressed.connect(_on_item_pressed.bind(id))
return btn
func _on_item_pressed(id: String) -> void:
EventBus.conversation_switch_requested.emit(id)
collapse()
func _on_conversation_changed(_id: String) -> void:
if _expanded:
refresh()
func _get_conversations() -> Array[Dictionary]:
var index: Dictionary = AppState.get_section("conversations")
var raw: Array = index.get("list", [])
var list: Array[Dictionary] = []
for i: int in range(raw.size()):
var entry: Variant = raw[i]
if entry is Dictionary:
list.append(entry)
return list
func _get_active_id() -> String:
var index: Dictionary = AppState.get_section("conversations")
var active: Variant = index.get("active_id", "")
return str(active)