Skip to content

Advanced documentation

V0rT3x edited this page Oct 23, 2025 · 3 revisions

Advanced documentation


Interacting with Fancygotchi from other plugins

Fancygotchi exposes a powerful mechanism for other plugins to dynamically interact with and modify the user interface. This is primarily achieved through two attributes injected into the ui object:

  1. ui._update: A dictionary that acts as a command queue to request UI changes.
  2. ui.fancy._state: A read-only dictionary that provides the current state of all theme widgets managed by Fancygotchi.

Let's explore how to use these, using the fancyshow.py plugin as a guide.

1. Requesting UI Changes with ui._update

To request a change to a widget's properties (like its position, color, or font), you need to populate the ui._update dictionary with specific keys. Fancygotchi checks this dictionary on every on_ui_update cycle and applies the requested changes.

The structure of the ui._update dictionary for a partial update is as follows:

ui._update.update({
    'update': True,      # Required: Signals that an update is pending.
    'partial': True,     # Required: Specifies that this is a partial theme update, not a full reload.
    'dict_part': {       # A dictionary containing the changes.
        'widget': {
            'name_of_widget': {
                'property_to_change': new_value,
                'another_property': another_value
            }
        }
    }
})

Example from fancyshow.py:

The fancyshow plugin dynamically changes the position and color of the name widget.

# From fancyshow.py

# The new widget properties we want to apply.
self.widget_options = {
    'position': ["center", "center"],
    'color': ["lime", "red"]
}

# ... inside on_ui_update(self, ui) ...

# Request a partial theme update to change the 'name' widget.
ui._update.update({
    'update': True,
    'partial': True,
    'dict_part': {'widget': {'name': self.widget_options}}
})
self.position_set = True
logging.info(f"fancyshow: setting 'name' options to {self.widget_options}")

In this snippet:

  • 'update': True tells Fancygotchi to process an update.
  • 'partial': True ensures that only the specified properties are changed, leaving the rest of the theme intact.
  • 'dict_part' contains the payload. It specifies that for the 'widget' named 'name', its properties should be updated according to the self.widget_options dictionary.

After Fancygotchi processes this update, it will automatically reset ui._update['update'] to False. Your plugin should check this flag to avoid sending redundant update requests on every cycle.

2. Reading the Current UI State with ui.fancy._state

To make intelligent changes, your plugin needs to know the current state of the UI. Fancygotchi provides a read-only snapshot of its internal theme configuration via ui.fancy._state.

This is crucial for:

  • Getting Initial Values: Before modifying a widget, you should store its original properties to revert them later (e.g., when your plugin is unloaded).
  • Verifying Changes: After requesting an update, you can check ui.fancy._state on subsequent cycles to confirm that your changes have been applied.
  • Adapting to Other Changes: If another plugin or a theme reload changes the UI, your plugin can detect this by comparing the current state with its expected state.

The ui.fancy._state is a dictionary where keys are widget names, and values are dictionaries of their properties.

Example from fancyshow.py:

  1. Getting the original properties: The _get_initial_options helper function reads the initial state of the properties it intends to modify.

    # From fancyshow.py
    def _get_initial_options(self, ui):
        # `ui.fancy._state` is a special attribute injected by Fancygotchi
        if hasattr(ui, 'fancy') and hasattr(ui.fancy, '_state') and 'name' in ui.fancy._state:
            name_state = ui.fancy._state['name']
            new_original_options = {}
            # We only care about the properties we intend to modify.
            for prop in self.widget_options.keys():
                if prop in name_state:
                    current_original_value = name_state.get(prop)
                    new_original_options[prop] = current_original_value
            
            if not self.original_widget_options:
                self.original_widget_options = new_original_options
                logging.info(f"fancyshow: Stored initial 'name' options: {self.original_widget_options}")
  2. Verifying changes have been applied: After setting the new position, the plugin continuously verifies that the name widget's properties match what it expects.

    # From fancyshow.py
    if self.position_set and hasattr(ui, 'fancy') and hasattr(ui.fancy, '_state') and 'name' in ui.fancy._state:
        name_state = ui.fancy._state['name']
        for prop, expected_value in self.widget_options.items():
            current_value = name_state.get(prop)
            # Normalize for comparison
            if isinstance(expected_value, list): expected_value = tuple(expected_value)
            if isinstance(current_value, list): current_value = tuple(current_value)
            
            if current_value != expected_value:
                # Handle the discrepancy...
  3. Reverting changes on unload: The on_unload method uses the stored self.original_widget_options to request another partial update, reverting the widget to its original state.

    # From fancyshow.py
    def on_unload(self, ui):
        logging.info("fancyshow plugin unloading...")
        self.reverting = True
        if self.original_widget_options and hasattr(ui, '_update'):
            # Use Fancygotchi's partial update mechanism to revert the 'name' widget.
            ui._update.update({
                'update': True,
                'partial': True,
                'dict_part': {'widget': {'name': self.original_widget_options}}
            })
            logging.info(f"fancyshow: attempting to revert 'name' options to {self.original_widget_options}")
            # ... includes logic to wait and verify the reversion.

By using ui._update to write changes and ui.fancy._state to read them, you can create robust plugins that interact cleanly and predictably with the Fancygotchi theme engine.

Clone this wiki locally