Files
2025-11-24 23:10:55 -06:00
..
2025-11-24 23:10:55 -06:00

Reusable UI Components

This directory contains reusable UI components for Code of Conquest.

Available Components

CustomButton

Script: scripts/components/custom_button.gd

Enhanced button with multiple visual variants and features.

Features:

  • Multiple variants (Primary, Secondary, Danger, Success, Ghost)
  • Icon support (left or right position)
  • Loading state
  • Hover effects
  • Themed styling

Usage:

var btn = CustomButton.new()
btn.text = "Login"
btn.set_variant(CustomButton.Variant.PRIMARY)
btn.button_clicked.connect(_on_login_clicked)
add_child(btn)

Variants:

  • PRIMARY - Gold accent button for main actions
  • SECONDARY - Standard button
  • DANGER - Red button for destructive actions (delete, etc.)
  • SUCCESS - Green button for positive actions
  • GHOST - Transparent/subtle button

Card

Script: scripts/components/card.gd

Container with optional header and footer, styled like the web UI cards.

Features:

  • Optional header with text
  • Body content area
  • Optional footer with buttons
  • Multiple style variants
  • Automatic sizing

Usage:

var card = Card.new()
card.set_header("Character Details")

var content = Label.new()
content.text = "Character information goes here"
card.add_content(content)

card.set_footer_buttons(["Save", "Cancel"])
card.footer_button_pressed.connect(_on_card_button_pressed)

add_child(card)

Style Variants:

  • DEFAULT - Standard card with subtle border
  • HIGHLIGHTED - Gold border with shadow
  • SUBTLE - Minimal border

FormField

Script: scripts/components/form_field.gd

Form input field with label, validation, and error display.

Features:

  • Label + input + error message
  • Multiple input types (text, email, password, number, phone)
  • Built-in validation
  • Required field support
  • Min/max length validation
  • Error state styling

Usage:

var email_field = FormField.new()
email_field.set_label("Email")
email_field.set_placeholder("Enter your email")
email_field.input_type = FormField.InputType.EMAIL
email_field.required = true
email_field.value_changed.connect(_on_email_changed)

add_child(email_field)

# Later, validate
if email_field.validate():
    var email = email_field.get_value()
    # Process email

Input Types:

  • TEXT - Plain text
  • EMAIL - Email validation
  • PASSWORD - Hidden password
  • NUMBER - Numeric only
  • PHONE - Phone number format

Creating New Components

1. Create Script

Create a new GDScript file in scripts/components/:

extends Control  # or appropriate base class
class_name MyComponent

## MyComponent
##
## Brief description of what this component does
##
## Usage:
##   var comp = MyComponent.new()
##   comp.some_property = "value"

signal some_signal(data: String)

@export var some_property: String = ""

func _ready() -> void:
    _setup_structure()
    _apply_styling()

func _setup_structure() -> void:
    # Build node hierarchy
    pass

func _apply_styling() -> void:
    # Apply theme colors and styleboxes
    pass

2. Use Theme Colors

Always use ThemeColors constants:

var style = StyleBoxFlat.new()
style.bg_color = ThemeColors.BACKGROUND_CARD
style.border_color = ThemeColors.BORDER_DEFAULT

3. Make It Configurable

Use @export variables for Godot Inspector:

@export var title: String = ""
@export_enum("Small", "Medium", "Large") var size: String = "Medium"
@export var show_icon: bool = true

4. Emit Signals

For user interactions:

signal item_selected(item_id: String)
signal value_changed(new_value: Variant)

func _on_internal_action():
    item_selected.emit("some_id")

5. Document Usage

Include usage examples in docstring:

## MyComponent
##
## Detailed description of the component.
##
## Usage:
##   var comp = MyComponent.new()
##   comp.title = "My Title"
##   comp.item_selected.connect(_on_item_selected)
##   add_child(comp)

Component Patterns

Separation of Structure and Style

func _ready() -> void:
    _setup_structure()  # Build node hierarchy
    _apply_styling()    # Apply theme

func _setup_structure() -> void:
    # Create child nodes
    # Set up hierarchy
    # Connect signals
    pass

func _apply_styling() -> void:
    # Apply StyleBoxes
    # Set colors
    # Set fonts
    pass

Responsive Design

func _apply_styling() -> void:
    # Check platform
    var is_mobile = OS.get_name() in ["Android", "iOS"]

    if is_mobile:
        # Larger touch targets
        custom_minimum_size = Vector2(60, 60)
    else:
        # Desktop sizing
        custom_minimum_size = Vector2(40, 40)

Validation Pattern

func validate() -> bool:
    var is_valid = true

    # Check conditions
    if some_condition:
        show_error("Error message")
        is_valid = false

    if is_valid:
        clear_error()

    return is_valid

Testing Components

Create a test scene test_components.tscn:

  1. Add each component
  2. Test all variants
  3. Test all states (normal, hover, pressed, disabled)
  4. Test on different screen sizes
  5. Test on different platforms

Example test scene structure:

Control (root)
├─ VBoxContainer
│  ├─ Label ("Buttons")
│  ├─ HBoxContainer
│  │  ├─ CustomButton (Primary)
│  │  ├─ CustomButton (Secondary)
│  │  ├─ CustomButton (Danger)
│  │  └─ CustomButton (Ghost)
│  ├─ Label ("Cards")
│  ├─ HBoxContainer
│  │  ├─ Card (Default)
│  │  ├─ Card (Highlighted)
│  │  └─ Card (Subtle)
│  └─ Label ("Form Fields")
│     ├─ FormField (Text)
│     ├─ FormField (Email)
│     └─ FormField (Password)

Best Practices

  1. Always use ThemeColors - Never hardcode colors
  2. Make components reusable - Avoid scene-specific logic
  3. Use signals for communication - Don't couple components
  4. Document everything - Docstrings + usage examples
  5. Test on all platforms - Desktop, mobile, web
  6. Follow naming conventions - PascalCase for class names
  7. Export important properties - Make them editable in Inspector
  8. Validate inputs - Check types and ranges
  9. Handle edge cases - Empty strings, null values, etc.
  10. Keep it simple - One component, one responsibility

Common Pitfalls

Don't hardcode colors

var style = StyleBoxFlat.new()
style.bg_color = Color("#1a1a2e")  # BAD

Use ThemeColors

var style = StyleBoxFlat.new()
style.bg_color = ThemeColors.BACKGROUND_PRIMARY  # GOOD

Don't create nodes in every frame

func _process(delta):
    var button = Button.new()  # BAD - leaks memory
    add_child(button)

Create once in _ready

var button: Button

func _ready():
    button = Button.new()
    add_child(button)

Don't couple components

# In a button component
func _on_pressed():
    get_parent().get_node("SomeOtherNode").do_something()  # BAD

Use signals

signal action_requested

func _on_pressed():
    action_requested.emit()  # GOOD

Resources