Files
skelly/docs/UI_COMPONENTS.md
Vladimir nett00n Budylnikov ea8c85d7ad feature/match3/move-gems (#7)
Reviewed-on: #7
Co-authored-by: Vladimir nett00n Budylnikov <git@nett00n.org>
Co-committed-by: Vladimir nett00n Budylnikov <git@nett00n.org>
2025-09-25 11:48:08 +02:00

8.6 KiB
Raw Permalink Blame History

UI Components

This document describes the custom UI components available in the Skelly project.

ValueStepper Component

Overview

ValueStepper is a reusable UI control for stepping through discrete values with left/right arrow navigation. It provides an intuitive interface for selecting from predefined options and is particularly well-suited for game settings menus.

Location: scenes/ui/components/ValueStepper.tscn and scenes/ui/components/ValueStepper.gd

Why ValueStepper Exists

Godot's built-in controls have limitations for discrete option selection:

  • OptionButton: Dropdown popups don't work well with gamepad navigation
  • SpinBox: Designed for numeric values, not text options
  • HSlider: Better for continuous values, not discrete choices

ValueStepper fills this gap by providing:

  • Gamepad-friendly discrete option selection
  • No popup complications - values displayed inline
  • Dual input support - mouse clicks + keyboard/gamepad
  • Clean horizontal layout with current value always visible
  • Perfect for game settings like language, difficulty, resolution

Features

  • Multiple Data Sources: Built-in support for language, resolution, difficulty
  • Custom Values: Easy setup with custom arrays of values
  • Navigation Integration: Built-in highlighting and input handling
  • Signal-Based: Clean event communication with parent scenes
  • Visual Feedback: Automatic highlighting and animations
  • Audio Support: Integrated click sounds
  • Flexible Display: Separate display names and internal values

Visual Structure

[<] [Current Value] [>]
  • Left Arrow Button (<): Navigate to previous value
  • Value Display: Shows current selection (e.g., "English", "Hard", "1920×1080")
  • Right Arrow Button (>): Navigate to next value

API Reference

Signals

signal value_changed(new_value: String, new_index: int)

Emitted when the value changes, providing both the new value string and its index.

Properties

@export var data_source: String = "language"
@export var custom_format_function: String = ""
  • data_source: Determines the data type ("language", "resolution", "difficulty", or "custom")
  • custom_format_function: Reserved for future custom formatting (currently unused)

Key Methods

Setup and Configuration

func setup_custom_values(custom_values: Array[String], custom_display_names: Array[String] = [])

Configure the stepper with custom values and optional display names.

Value Management

func get_current_value() -> String
func set_current_value(value: String)
func change_value(direction: int)

Get, set, or modify the current value programmatically.

Navigation Integration

func set_highlighted(highlighted: bool)
func handle_input_action(action: String) -> bool
func get_control_name() -> String

Integration methods for navigation systems and visual feedback.

Usage Examples

Basic Usage in Scene

  1. Add to Scene: Instance ValueStepper.tscn in your scene
  2. Set Data Source: Configure the data_source property
  3. Connect Signal: Connect the value_changed signal
# In your scene script
@onready var language_stepper: ValueStepper = $LanguageStepper

func _ready():
    language_stepper.value_changed.connect(_on_language_changed)

func _on_language_changed(new_value: String, new_index: int):
    print("Language changed to: ", new_value)

Built-in Data Sources

Language Selection

# Set data_source = "language" in editor or code
language_stepper.data_source = "language"

Automatically loads available languages from SettingsManager.

Resolution Selection

# Set data_source = "resolution"
resolution_stepper.data_source = "resolution"

Provides common resolution options with display names.

Difficulty Selection

# Set data_source = "difficulty"
difficulty_stepper.data_source = "difficulty"

Provides difficulty levels: Easy, Normal, Hard, Nightmare.

Custom Values

# Setup custom theme selector
var theme_values = ["light", "dark", "blue", "green"]
var theme_names = ["Light Theme", "Dark Theme", "Blue Theme", "Green Theme"]
theme_stepper.setup_custom_values(theme_values, theme_names)
theme_stepper.data_source = "theme"  # For better logging

Navigation System Integration

# In a navigation-enabled menu
var navigable_controls: Array[Control] = []

func _setup_navigation():
    navigable_controls.append(volume_slider)
    navigable_controls.append(language_stepper)  # Add stepper to navigation
    navigable_controls.append(back_button)

func _update_visual_selection():
    for i in range(navigable_controls.size()):
        var control = navigable_controls[i]
        if control is ValueStepper:
            control.set_highlighted(i == current_index)
        else:
            # Handle other control highlighting
            pass

func _handle_input(action: String):
    var current_control = navigable_controls[current_index]
    if current_control is ValueStepper:
        if current_control.handle_input_action(action):
            AudioManager.play_ui_click()
            return true
    return false

Integration Patterns

Settings Menu Pattern

See scenes/ui/SettingsMenu.gd for a complete example of integrating ValueStepper into a settings menu with full navigation support.

Multiple Steppers Navigation

See examples/ValueStepperExample.gd for an example showing multiple steppers with keyboard/gamepad navigation.

Extending ValueStepper

Adding New Data Sources

  1. Add to _load_data() method:
func _load_data():
    match data_source:
        "language":
            _load_language_data()
        "your_custom_type":
            _load_your_custom_data()
        # ... other cases
  1. Implement your loader:
func _load_your_custom_data():
    values = ["value1", "value2", "value3"]
    display_names = ["Display 1", "Display 2", "Display 3"]
    current_index = 0
  1. Add value application logic:
func _apply_value_change(new_value: String, index: int):
    match data_source:
        "your_custom_type":
            # Apply your custom logic here
            YourManager.set_custom_setting(new_value)

Custom Formatting

Override _update_display() for custom display formatting:

func _update_display():
    if data_source == "your_custom_type":
        # Custom formatting logic
        value_display.text = "Custom: " + display_names[current_index]
    else:
        super._update_display()  # Call parent implementation

Best Practices

When to Use ValueStepper

  • Discrete options: Language, difficulty, resolution, theme
  • Settings menus: Any option with predefined choices
  • Game configuration: Graphics quality, control schemes
  • Limited options: 2-10 options work best

When NOT to Use ValueStepper

  • Continuous values: Use sliders for volume, brightness
  • Large lists: Use ItemList or OptionButton for 20+ items
  • Text input: Use LineEdit for user-entered text
  • Numeric input: Use SpinBox for number entry

Performance Considerations

  • ValueStepper is lightweight and suitable for multiple instances
  • Data loading happens once in _ready()
  • Visual updates are minimal (just text changes)

Accessibility

  • Visual highlighting provides clear focus indication
  • Audio feedback confirms user actions
  • Keyboard and gamepad support for non-mouse users
  • Consistent navigation patterns

Common Issues and Solutions

Stepper Not Responding to Input

  • Ensure handle_input_action() is called from parent's _input()
  • Check that the stepper has proper focus/highlighting
  • Verify input actions are defined in project input map

Values Not Saving

  • Override _apply_value_change() to handle persistence
  • Connect to value_changed signal for custom save logic
  • Ensure SettingsManager or your data manager is configured

Display Names Not Showing

  • Check that display_names array is properly populated
  • Ensure display_names.size() matches values.size()
  • Verify _update_display() is called after data loading

File Structure

scenes/ui/components/
├── ValueStepper.gd          # Main component script
└── ValueStepper.tscn        # Component scene

examples/
├── ValueStepperExample.gd   # Usage example script
└── ValueStepperExample.tscn # Example scene

docs/
└── UI_COMPONENTS.md         # This documentation

This component provides a solid foundation for any game's settings system and can be easily extended for project-specific needs.