152 lines
4.1 KiB
GDScript
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)
|