# 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**: ```gdscript 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**: ```gdscript 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**: ```gdscript 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/`: ```gdscript 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: ```gdscript 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: ```gdscript @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: ```gdscript 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: ```gdscript ## 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 ```gdscript 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 ```gdscript 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 ```gdscript 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 ```gdscript var style = StyleBoxFlat.new() style.bg_color = Color("#1a1a2e") # BAD ``` ### ✅ Use ThemeColors ```gdscript var style = StyleBoxFlat.new() style.bg_color = ThemeColors.BACKGROUND_PRIMARY # GOOD ``` ### ❌ Don't create nodes in every frame ```gdscript func _process(delta): var button = Button.new() # BAD - leaks memory add_child(button) ``` ### ✅ Create once in _ready ```gdscript var button: Button func _ready(): button = Button.new() add_child(button) ``` ### ❌ Don't couple components ```gdscript # In a button component func _on_pressed(): get_parent().get_node("SomeOtherNode").do_something() # BAD ``` ### ✅ Use signals ```gdscript signal action_requested func _on_pressed(): action_requested.emit() # GOOD ``` ## Resources - [Godot UI Documentation](https://docs.godotengine.org/en/stable/tutorials/ui/index.html) - [Control Nodes](https://docs.godotengine.org/en/stable/classes/class_control.html) - [Themes](https://docs.godotengine.org/en/stable/tutorials/ui/gui_using_theme_editor.html) - [StyleBox](https://docs.godotengine.org/en/stable/classes/class_stylebox.html)