336 lines
7.4 KiB
Markdown
336 lines
7.4 KiB
Markdown
# 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)
|