first commit
This commit is contained in:
335
godot_client/scenes/components/README.md
Normal file
335
godot_client/scenes/components/README.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user