203 lines
5.2 KiB
GDScript
203 lines
5.2 KiB
GDScript
extends PanelContainer
|
|
class_name Card
|
|
## Card Component
|
|
##
|
|
## Styled container for content with optional header and footer.
|
|
## Mimics the card design from the web UI.
|
|
##
|
|
## Structure:
|
|
## Card (PanelContainer)
|
|
## └─ VBoxContainer
|
|
## ├─ Header (optional)
|
|
## ├─ Body (content)
|
|
## └─ Footer (optional)
|
|
##
|
|
## Usage:
|
|
## var card = Card.new()
|
|
## card.set_header("Character Details")
|
|
## card.add_content(my_content_node)
|
|
## card.set_footer_buttons(["Save", "Cancel"])
|
|
|
|
signal header_action_pressed(action: String)
|
|
signal footer_button_pressed(button_text: String)
|
|
|
|
# Export variables
|
|
@export var header_text: String = ""
|
|
@export var show_header: bool = false
|
|
@export var show_footer: bool = false
|
|
@export var card_style: StyleVariant = StyleVariant.DEFAULT
|
|
|
|
# Style variants
|
|
enum StyleVariant {
|
|
DEFAULT, # Standard card
|
|
HIGHLIGHTED, # Gold border highlight
|
|
SUBTLE # Minimal border
|
|
}
|
|
|
|
# Internal nodes
|
|
var _container: VBoxContainer = null
|
|
var _header_container: HBoxContainer = null
|
|
var _header_label: Label = null
|
|
var _body_container: MarginContainer = null
|
|
var _body_content: VBoxContainer = null
|
|
var _footer_container: HBoxContainer = null
|
|
|
|
|
|
func _ready() -> void:
|
|
_setup_structure()
|
|
_apply_style()
|
|
|
|
if not header_text.is_empty():
|
|
set_header(header_text)
|
|
|
|
|
|
## Setup card structure
|
|
func _setup_structure() -> void:
|
|
# Main container
|
|
_container = VBoxContainer.new()
|
|
add_child(_container)
|
|
|
|
# Header
|
|
_header_container = HBoxContainer.new()
|
|
_header_container.visible = show_header
|
|
|
|
_header_label = Label.new()
|
|
_header_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
# TODO: Set heading font when theme is set up
|
|
_header_container.add_child(_header_label)
|
|
|
|
_container.add_child(_header_container)
|
|
|
|
# Add separator after header
|
|
var separator = HSeparator.new()
|
|
separator.visible = show_header
|
|
_container.add_child(separator)
|
|
|
|
# Body
|
|
_body_container = MarginContainer.new()
|
|
_body_container.add_theme_constant_override("margin_left", 16)
|
|
_body_container.add_theme_constant_override("margin_right", 16)
|
|
_body_container.add_theme_constant_override("margin_top", 16)
|
|
_body_container.add_theme_constant_override("margin_bottom", 16)
|
|
|
|
_body_content = VBoxContainer.new()
|
|
_body_content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_body_content.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
|
|
_body_container.add_child(_body_content)
|
|
_container.add_child(_body_container)
|
|
|
|
# Footer
|
|
var footer_separator = HSeparator.new()
|
|
footer_separator.visible = show_footer
|
|
_container.add_child(footer_separator)
|
|
|
|
_footer_container = HBoxContainer.new()
|
|
_footer_container.visible = show_footer
|
|
_footer_container.alignment = BoxContainer.ALIGNMENT_END
|
|
_container.add_child(_footer_container)
|
|
|
|
|
|
## Apply card styling
|
|
func _apply_style() -> void:
|
|
var style = StyleBoxFlat.new()
|
|
|
|
match card_style:
|
|
StyleVariant.DEFAULT:
|
|
style.bg_color = ThemeColors.BACKGROUND_CARD
|
|
style.border_width_all = 1
|
|
style.border_color = ThemeColors.BORDER_DEFAULT
|
|
style.corner_radius_all = 8
|
|
|
|
StyleVariant.HIGHLIGHTED:
|
|
style.bg_color = ThemeColors.BACKGROUND_CARD
|
|
style.border_width_all = 2
|
|
style.border_color = ThemeColors.GOLD_ACCENT
|
|
style.corner_radius_all = 8
|
|
style.shadow_color = ThemeColors.SHADOW
|
|
style.shadow_size = 4
|
|
|
|
StyleVariant.SUBTLE:
|
|
style.bg_color = ThemeColors.BACKGROUND_CARD
|
|
style.border_width_all = 1
|
|
style.border_color = ThemeColors.DIVIDER
|
|
style.corner_radius_all = 4
|
|
|
|
add_theme_stylebox_override("panel", style)
|
|
|
|
|
|
## Set header text and show header
|
|
func set_header(text: String) -> void:
|
|
header_text = text
|
|
_header_label.text = text
|
|
_header_container.visible = true
|
|
show_header = true
|
|
|
|
# Show separator
|
|
if _container.get_child_count() > 1:
|
|
_container.get_child(1).visible = true
|
|
|
|
|
|
## Hide header
|
|
func hide_header() -> void:
|
|
_header_container.visible = false
|
|
show_header = false
|
|
|
|
# Hide separator
|
|
if _container.get_child_count() > 1:
|
|
_container.get_child(1).visible = false
|
|
|
|
|
|
## Add content to card body
|
|
func add_content(node: Node) -> void:
|
|
_body_content.add_child(node)
|
|
|
|
|
|
## Clear all content from body
|
|
func clear_content() -> void:
|
|
for child in _body_content.get_children():
|
|
child.queue_free()
|
|
|
|
|
|
## Set footer buttons
|
|
func set_footer_buttons(button_labels: Array[String]) -> void:
|
|
# Clear existing buttons
|
|
for child in _footer_container.get_children():
|
|
child.queue_free()
|
|
|
|
# Add new buttons
|
|
for label in button_labels:
|
|
var btn = Button.new()
|
|
btn.text = label
|
|
btn.pressed.connect(_on_footer_button_pressed.bind(label))
|
|
_footer_container.add_child(btn)
|
|
|
|
# Show footer
|
|
_footer_container.visible = true
|
|
show_footer = true
|
|
|
|
# Show separator
|
|
if _container.get_child_count() > 2:
|
|
_container.get_child(_container.get_child_count() - 2).visible = true
|
|
|
|
|
|
## Hide footer
|
|
func hide_footer() -> void:
|
|
_footer_container.visible = false
|
|
show_footer = false
|
|
|
|
# Hide separator
|
|
if _container.get_child_count() > 2:
|
|
_container.get_child(_container.get_child_count() - 2).visible = false
|
|
|
|
|
|
## Set card style variant
|
|
func set_style_variant(variant: StyleVariant) -> void:
|
|
card_style = variant
|
|
_apply_style()
|
|
|
|
|
|
## Internal: Handle footer button press
|
|
func _on_footer_button_pressed(button_text: String) -> void:
|
|
footer_button_pressed.emit(button_text)
|