Skip to content

EventHandler

Module: terminaltexteffects.engine.base_character

Register and handle events related to a character or a body.

Events related to node state changes (e.g. scene complete) can be registered with the EventHandler. When an event is triggered, the EventHandler will take the specified action (e.g. activate a Path). Every character and every body has one.

A body's handler takes the node actions (ACTIVATE_PATH, DEACTIVATE_PATH, SET_COORDINATE, ATTACH, DETACH, CALLBACK) and the path and attach events. Scenes, layers and appearance belong to characters, so registering those on a body raises EventRegistrationNodeError.

Attributes:

Name Type Description
character EffectCharacter | Body

The node whose events are handled by this EventHandler.

registered_events dict[tuple[Event, Scene | Waypoint | Path | Body], list[tuple[Action, Scene | Waypoint | Path | Body | int | Coord | Vec3 | Callback | str | None]]]

Registered event/action mappings keyed by the triggering event and caller object.

Note

SEGMENT_ENTERED/EXITED events will trigger the first time the character enters or exits a segment. If looping, each loop will trigger the event, but not backwards motion as is possible with the bounce easing functions.

Source code in terminaltexteffects/engine/base_character.py
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
class EventHandler:
    """Register and handle events related to a character or a body.

    Events related to node state changes (e.g. scene complete) can be registered with the EventHandler.
    When an event is triggered, the EventHandler will take the specified action (e.g. activate a Path).
    Every character and every body has one.

    A body's handler takes the node actions (`ACTIVATE_PATH`, `DEACTIVATE_PATH`, `SET_COORDINATE`, `ATTACH`,
    `DETACH`, `CALLBACK`) and the path and attach events. Scenes, layers and appearance belong to characters, so
    registering those on a body raises `EventRegistrationNodeError`.

    Attributes:
        character (EffectCharacter | Body): The node whose events are handled by this EventHandler.
        registered_events: Registered event/action mappings keyed by the triggering event and caller object.

    Note:
        SEGMENT_ENTERED/EXITED events will trigger the first time the character enters or exits a segment.
        If looping, each loop will trigger the event, but not backwards motion as is possible with
        the bounce easing functions.

    """

    def __init__(self, character: EffectCharacter | world.Body) -> None:
        """Initialize the instance with the node it handles events for.

        Args:
            character (EffectCharacter | world.Body): The character or body for which the EventHandler is handling
                events.

        """
        # Typed as a character: a body's handler only takes the node actions, which bodies have too.
        self.character: EffectCharacter = typing.cast("EffectCharacter", character)
        self.registered_events: dict[
            tuple[EventHandler.Event, animation.Scene | motion.Waypoint | motion.Path | world.Body],
            list[
                tuple[
                    EventHandler.Action,
                    animation.Scene
                    | motion.Waypoint
                    | motion.Path
                    | world.Body
                    | int
                    | Coord
                    | Vec3
                    | EventHandler.Callback
                    | str
                    | None,
                ]
            ],
        ] = {}

    class Event(Enum):
        """An Event that can be registered with the EventHandler.

        Register Events with the EventHandler using the register_event method of the EventHandler class.

        Attributes:
            SEGMENT_ENTERED (Event): A path segment has been entered.
            SEGMENT_EXITED (Event): A path segment has been exited.
            PATH_ACTIVATED (Event): A path has been activated.
            PATH_COMPLETE (Event): A path has been completed.
            PATH_HOLDING (Event): A path has entered the holding state.
            SCENE_ACTIVATED (Event): An animation scene has been activated.
            SCENE_COMPLETE (Event): An animation scene has completed.
            ATTACHED (Event): The node was attached to a body (not the world root). The caller is the new parent.
            DETACHED (Event): The node left a body (not the world root), by `attach` to another or `detach`. The
                caller is the old parent.

        """

        SEGMENT_ENTERED = auto()
        SEGMENT_EXITED = auto()
        PATH_ACTIVATED = auto()
        PATH_COMPLETE = auto()
        PATH_HOLDING = auto()
        SCENE_ACTIVATED = auto()
        SCENE_COMPLETE = auto()
        ATTACHED = auto()
        DETACHED = auto()

    class Action(Enum):
        """Actions that can be taken when an event is triggered.

        An Action is taken when an Event is triggered. Register Actions with the EventHandler using the
        register_event method of the EventHandler class.

        Attributes:
            ACTIVATE_PATH (Action): Activates a path. The action target is the path itself or its ID.
            ACTIVATE_SCENE (Action): Activates an animation scene. The action target is the scene itself or its ID.
            DEACTIVATE_PATH (Action): Deactivates a path. The action target is the path itself, its ID,
                or None to deactivate the currently active path.
            DEACTIVATE_SCENE (Action): Deactivates an animation scene. The action target is the scene itself,
                its ID, or None to deactivate the currently active scene.
            RESET_APPEARANCE (Action): Resets the appearance of the character to the input symbol and color.
            SET_LAYER (Action): Sets the layer of the character. The action target is the layer number.
            SET_COORDINATE (Action): Moves the node, now. The action target is a `Coord`, a cell in the parent's
                frame that keeps the node's height, or a `Vec3`, a whole position in the parent's frame.
            CALLBACK (Action): Calls a callback function. The action target is an EventHandler.Callback object.
            ATTACH (Action): Attaches the node to a body, keeping its place in the world. The action target is the
                body.
            DETACH (Action): Detaches the node into the world, keeping its place. Takes no target.

        """

        ACTIVATE_PATH = auto()
        ACTIVATE_SCENE = auto()
        DEACTIVATE_PATH = auto()
        DEACTIVATE_SCENE = auto()
        RESET_APPEARANCE = auto()
        SET_LAYER = auto()
        SET_COORDINATE = auto()
        CALLBACK = auto()
        ATTACH = auto()
        DETACH = auto()

    @dataclass(init=False)
    class Callback:
        """A callback action target that can be taken when an event is triggered.

        Register callback actions with the EventHandler using the register_event method of the EventHandler class.

        The callback function will be called with the character and any additional arguments when the event
        is triggered. The character will be the first argument passed to the callback function followed by any
        additional arguments in the order they were passed to the Callback object.

        Example:
            Create a callback to set the character's visibility to False. The following code would be
            used within an effect where 'self' is the EffectIterator instance:

                cb = EventHandler.Callback(lambda c: self.terminal.set_character_visibility(c, is_visible=False))

        """

        callback: typing.Callable
        args: tuple[typing.Any, ...]

        def __init__(self, callback: typing.Callable, *args: typing.Any) -> None:
            """Initialize the instance with the callback function and arguments.

            Args:
                callback (typing.Callable): The callback function to call.
                args (tuple[typing.Any,...]): A tuple of arguments to pass to the callback function. The first argument
                    will be the character, followed by any additional arguments.

            """
            self.callback = callback
            self.args = args

    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.ACTIVATE_SCENE],
        target: animation.Scene | str,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.DEACTIVATE_SCENE],
        target: animation.Scene | str | None = ...,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.ACTIVATE_PATH],
        target: motion.Path | str,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.DEACTIVATE_PATH],
        target: motion.Path | str | None = ...,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.SET_COORDINATE],
        target: Coord | Vec3,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.SET_LAYER],
        target: int,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.CALLBACK],
        target: Callback,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.RESET_APPEARANCE],
        target: None = None,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.ATTACH],
        target: world.Body,
    ) -> None: ...
    @typing.overload
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: typing.Literal[Action.DETACH],
        target: None = None,
    ) -> None: ...
    def register_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
        action: Action,
        target: animation.Scene | motion.Path | world.Body | int | Coord | Vec3 | Callback | str | None = None,
    ) -> None:
        """Register an event to be handled by the EventHandler.

        Note: Action.RESET_APPEARANCE does not accept a target.
        For `PATH_ACTIVATED`, `PATH_COMPLETE`, `SCENE_ACTIVATED`, and `SCENE_COMPLETE`,
        `caller` may be the triggering object itself or its registered ID string.
        `PATH_HOLDING` and segment events require their corresponding object instances.
        For `ACTIVATE_PATH`, `DEACTIVATE_PATH`, `ACTIVATE_SCENE`, and `DEACTIVATE_SCENE`,
        string targets are resolved from registered path or scene IDs. `DEACTIVATE_PATH`,
        `DEACTIVATE_SCENE`, and `RESET_APPEARANCE` may use `None` where supported by the action.
        `ATTACHED` and `DETACHED` take the parent body as the caller.

        Args:
            event (Event): The event to register.
            caller (animation.Scene | motion.Waypoint | motion.Path | world.Body | str): The object
                that triggers the event.
            action (Action): The action to take when the event is triggered.
            target (animation.Scene | motion.Path | world.Body | int | Coord | Vec3 | Callback | str | None):
                The target of the action.

        Raises:
            EventRegistrationCallerError: If the caller object is not the required type for the
                specified event.
            EventRegistrationTargetError: If the target is not the correct type for the action or if
                ``Action.RESET_APPEARANCE`` is provided with a target.
            DuplicateEventRegistrationError: If the exact same event-caller-action-target combination
                has already been registered.

        Example:
            Register an event to activate a scene when a Path is complete:
            `event_handler.register_event(EventHandler.Event.PATH_COMPLETE, some_path,
                EventHandler.Action.ACTIVATE_SCENE, some_scene)`

        """
        if isinstance(self.character, world.Body) and (event in _CHARACTER_EVENTS or action not in _NODE_ACTIONS):
            raise EventRegistrationNodeError(event, action)

        # find caller Path when provided path_id
        if event in (EventHandler.Event.PATH_ACTIVATED, EventHandler.Event.PATH_COMPLETE) and isinstance(caller, str):
            if (path_query_result := self.character.motion.query_path(caller)) is None:
                raise PathNotFoundError(path_id=caller)
            caller = path_query_result

        # find caller Scene when provided scene_id
        elif event in (EventHandler.Event.SCENE_ACTIVATED, EventHandler.Event.SCENE_COMPLETE) and isinstance(
            caller,
            str,
        ):
            if (scene_query_result := self.character.animation.query_scene(caller)) is None:
                raise SceneNotFoundError(scene_id=caller)
            caller = scene_query_result

        if _EVENT_CALLER_TYPES[event] != caller.__class__:
            raise EventRegistrationCallerError(event, caller, _EVENT_CALLER_TYPES[event])

        # find target Path when provided path_id
        if action in (EventHandler.Action.ACTIVATE_PATH, EventHandler.Action.DEACTIVATE_PATH) and isinstance(
            target,
            str,
        ):
            if (path_query_result := self.character.motion.query_path(target)) is None:
                raise PathNotFoundError(path_id=target)
            target = path_query_result

        # find target Scene when provided scene_id
        elif action in (EventHandler.Action.ACTIVATE_SCENE, EventHandler.Action.DEACTIVATE_SCENE) and isinstance(
            target,
            str,
        ):
            if (scene_query_result := self.character.animation.query_scene(target)) is None:
                raise SceneNotFoundError(scene_id=target)
            target = scene_query_result

        elif (
            action in (EventHandler.Action.DEACTIVATE_PATH, EventHandler.Action.DEACTIVATE_SCENE) and target is None
        ) or (action is EventHandler.Action.SET_COORDINATE and type(target) is Vec3):
            pass

        elif action is EventHandler.Action.DETACH:
            if target is not None:
                raise EventRegistrationTargetError(action, target, type(None))

        elif (action is EventHandler.Action.RESET_APPEARANCE and target is not None) or (
            _ACTION_TARGET_TYPES[action] != target.__class__
        ):
            raise EventRegistrationTargetError(action, target, _ACTION_TARGET_TYPES[action])

        assert isinstance(caller, (motion.Path, animation.Scene, motion.Waypoint, world.Body))
        new_event = (event, caller)
        new_action = (action, target)

        if new_event not in self.registered_events:
            self.registered_events[new_event] = []

        # Check for duplicate event-action-target combination
        if new_action in self.registered_events[new_event]:
            raise DuplicateEventRegistrationError(event, caller, action, target)

        self.registered_events[new_event].append(new_action)

    def _handle_event(
        self,
        event: Event,
        caller: animation.Scene | motion.Waypoint | motion.Path | world.Body,
    ) -> None:
        """Handle a registered event by executing all associated actions.

        This method processes an event triggered by a caller object (Scene, Waypoint, or Path) and
        executes all actions that were registered for this specific event-caller combination. If no
        actions are registered for the given event and caller, the method returns without doing anything.

        The method supports the following action types:
        - ACTIVATE_PATH: Activates a motion path for the character
        - ACTIVATE_SCENE: Activates an animation scene for the character
        - DEACTIVATE_PATH: Deactivates a specific motion path or, when the target is `None`,
          the currently active path
        - DEACTIVATE_SCENE: Deactivates a specific animation scene or, when the target is `None`,
          the currently active scene
        - RESET_APPEARANCE: Resets the character's appearance to its input symbol and,
          where applicable, its configured input colors
        - SET_LAYER: Sets the character's rendering layer
        - SET_COORDINATE: Moves the node to a cell (`Coord`) or a position (`Vec3`) in its parent's frame
        - CALLBACK: Executes a custom callback function with the character and additional arguments

        Args:
            event (Event): The event to handle. Must be one of the Event enum values.
            caller (animation.Scene | motion.Waypoint | motion.Path | world.Body): The object that
                triggered the event.
                The caller type must match the expected type for the given event (e.g., PATH_COMPLETE
                events must be triggered by motion.Path objects).

        Note:
            This method is typically called internally by the animation and motion systems when
            specific state changes occur. Events and their associated actions are registered using
            the register_event method.

        Example:
            When `Event.PATH_COMPLETE` is triggered for a registered path caller, all
            actions registered for that event/caller pair are executed in order.

        """
        event_actions = self.registered_events.get((event, caller))
        if event_actions is None:
            return
        for action, target in event_actions:
            if action is EventHandler.Action.ACTIVATE_PATH:
                self.character.motion.activate_path(target)  # type: ignore[arg-type]
            elif action is EventHandler.Action.ACTIVATE_SCENE:
                self.character.animation.activate_scene(target)  # type: ignore[arg-type]
            elif action is EventHandler.Action.DEACTIVATE_PATH:
                if target is None:
                    self.character.motion.deactivate_path()
                else:
                    self.character.motion.deactivate_path(target)  # type: ignore[arg-type]
            elif action is EventHandler.Action.DEACTIVATE_SCENE:
                if target is None:
                    self.character.animation.deactivate_scene()
                else:
                    self.character.animation.deactivate_scene(target)  # type: ignore[arg-type]
            elif action is EventHandler.Action.RESET_APPEARANCE:
                self.character.animation.set_appearance(self.character.input_symbol)
            elif action is EventHandler.Action.SET_LAYER:
                self.character.layer = target  # type: ignore[assignment]
            elif action is EventHandler.Action.SET_COORDINATE:
                if type(target) is Vec3:
                    self.character.motion.position = target
                else:
                    self.character.motion.set_coordinate(target)  # type: ignore[arg-type]
            elif action is EventHandler.Action.CALLBACK:
                target.callback(self.character, *target.args)  # type: ignore[union-attr]
            elif action is EventHandler.Action.ATTACH:
                self.character.attach(target)  # type: ignore[arg-type]
            elif action is EventHandler.Action.DETACH:
                self.character.detach()

Action

Bases: Enum

Actions that can be taken when an event is triggered.

An Action is taken when an Event is triggered. Register Actions with the EventHandler using the register_event method of the EventHandler class.

Attributes:

Name Type Description
ACTIVATE_PATH Action

Activates a path. The action target is the path itself or its ID.

ACTIVATE_SCENE Action

Activates an animation scene. The action target is the scene itself or its ID.

DEACTIVATE_PATH Action

Deactivates a path. The action target is the path itself, its ID, or None to deactivate the currently active path.

DEACTIVATE_SCENE Action

Deactivates an animation scene. The action target is the scene itself, its ID, or None to deactivate the currently active scene.

RESET_APPEARANCE Action

Resets the appearance of the character to the input symbol and color.

SET_LAYER Action

Sets the layer of the character. The action target is the layer number.

SET_COORDINATE Action

Moves the node, now. The action target is a Coord, a cell in the parent's frame that keeps the node's height, or a Vec3, a whole position in the parent's frame.

CALLBACK Action

Calls a callback function. The action target is an EventHandler.Callback object.

ATTACH Action

Attaches the node to a body, keeping its place in the world. The action target is the body.

DETACH Action

Detaches the node into the world, keeping its place. Takes no target.

Source code in terminaltexteffects/engine/base_character.py
class Action(Enum):
    """Actions that can be taken when an event is triggered.

    An Action is taken when an Event is triggered. Register Actions with the EventHandler using the
    register_event method of the EventHandler class.

    Attributes:
        ACTIVATE_PATH (Action): Activates a path. The action target is the path itself or its ID.
        ACTIVATE_SCENE (Action): Activates an animation scene. The action target is the scene itself or its ID.
        DEACTIVATE_PATH (Action): Deactivates a path. The action target is the path itself, its ID,
            or None to deactivate the currently active path.
        DEACTIVATE_SCENE (Action): Deactivates an animation scene. The action target is the scene itself,
            its ID, or None to deactivate the currently active scene.
        RESET_APPEARANCE (Action): Resets the appearance of the character to the input symbol and color.
        SET_LAYER (Action): Sets the layer of the character. The action target is the layer number.
        SET_COORDINATE (Action): Moves the node, now. The action target is a `Coord`, a cell in the parent's
            frame that keeps the node's height, or a `Vec3`, a whole position in the parent's frame.
        CALLBACK (Action): Calls a callback function. The action target is an EventHandler.Callback object.
        ATTACH (Action): Attaches the node to a body, keeping its place in the world. The action target is the
            body.
        DETACH (Action): Detaches the node into the world, keeping its place. Takes no target.

    """

    ACTIVATE_PATH = auto()
    ACTIVATE_SCENE = auto()
    DEACTIVATE_PATH = auto()
    DEACTIVATE_SCENE = auto()
    RESET_APPEARANCE = auto()
    SET_LAYER = auto()
    SET_COORDINATE = auto()
    CALLBACK = auto()
    ATTACH = auto()
    DETACH = auto()

Callback dataclass

A callback action target that can be taken when an event is triggered.

Register callback actions with the EventHandler using the register_event method of the EventHandler class.

The callback function will be called with the character and any additional arguments when the event is triggered. The character will be the first argument passed to the callback function followed by any additional arguments in the order they were passed to the Callback object.

Example

Create a callback to set the character's visibility to False. The following code would be used within an effect where 'self' is the EffectIterator instance:

cb = EventHandler.Callback(lambda c: self.terminal.set_character_visibility(c, is_visible=False))
Source code in terminaltexteffects/engine/base_character.py
@dataclass(init=False)
class Callback:
    """A callback action target that can be taken when an event is triggered.

    Register callback actions with the EventHandler using the register_event method of the EventHandler class.

    The callback function will be called with the character and any additional arguments when the event
    is triggered. The character will be the first argument passed to the callback function followed by any
    additional arguments in the order they were passed to the Callback object.

    Example:
        Create a callback to set the character's visibility to False. The following code would be
        used within an effect where 'self' is the EffectIterator instance:

            cb = EventHandler.Callback(lambda c: self.terminal.set_character_visibility(c, is_visible=False))

    """

    callback: typing.Callable
    args: tuple[typing.Any, ...]

    def __init__(self, callback: typing.Callable, *args: typing.Any) -> None:
        """Initialize the instance with the callback function and arguments.

        Args:
            callback (typing.Callable): The callback function to call.
            args (tuple[typing.Any,...]): A tuple of arguments to pass to the callback function. The first argument
                will be the character, followed by any additional arguments.

        """
        self.callback = callback
        self.args = args

__init__(callback, *args)

Initialize the instance with the callback function and arguments.

Parameters:

Name Type Description Default
callback Callable

The callback function to call.

required
args tuple[Any, ...]

A tuple of arguments to pass to the callback function. The first argument will be the character, followed by any additional arguments.

()
Source code in terminaltexteffects/engine/base_character.py
def __init__(self, callback: typing.Callable, *args: typing.Any) -> None:
    """Initialize the instance with the callback function and arguments.

    Args:
        callback (typing.Callable): The callback function to call.
        args (tuple[typing.Any,...]): A tuple of arguments to pass to the callback function. The first argument
            will be the character, followed by any additional arguments.

    """
    self.callback = callback
    self.args = args

Event

Bases: Enum

An Event that can be registered with the EventHandler.

Register Events with the EventHandler using the register_event method of the EventHandler class.

Attributes:

Name Type Description
SEGMENT_ENTERED Event

A path segment has been entered.

SEGMENT_EXITED Event

A path segment has been exited.

PATH_ACTIVATED Event

A path has been activated.

PATH_COMPLETE Event

A path has been completed.

PATH_HOLDING Event

A path has entered the holding state.

SCENE_ACTIVATED Event

An animation scene has been activated.

SCENE_COMPLETE Event

An animation scene has completed.

ATTACHED Event

The node was attached to a body (not the world root). The caller is the new parent.

DETACHED Event

The node left a body (not the world root), by attach to another or detach. The caller is the old parent.

Source code in terminaltexteffects/engine/base_character.py
class Event(Enum):
    """An Event that can be registered with the EventHandler.

    Register Events with the EventHandler using the register_event method of the EventHandler class.

    Attributes:
        SEGMENT_ENTERED (Event): A path segment has been entered.
        SEGMENT_EXITED (Event): A path segment has been exited.
        PATH_ACTIVATED (Event): A path has been activated.
        PATH_COMPLETE (Event): A path has been completed.
        PATH_HOLDING (Event): A path has entered the holding state.
        SCENE_ACTIVATED (Event): An animation scene has been activated.
        SCENE_COMPLETE (Event): An animation scene has completed.
        ATTACHED (Event): The node was attached to a body (not the world root). The caller is the new parent.
        DETACHED (Event): The node left a body (not the world root), by `attach` to another or `detach`. The
            caller is the old parent.

    """

    SEGMENT_ENTERED = auto()
    SEGMENT_EXITED = auto()
    PATH_ACTIVATED = auto()
    PATH_COMPLETE = auto()
    PATH_HOLDING = auto()
    SCENE_ACTIVATED = auto()
    SCENE_COMPLETE = auto()
    ATTACHED = auto()
    DETACHED = auto()

__init__(character)

Initialize the instance with the node it handles events for.

Parameters:

Name Type Description Default
character EffectCharacter | Body

The character or body for which the EventHandler is handling events.

required
Source code in terminaltexteffects/engine/base_character.py
def __init__(self, character: EffectCharacter | world.Body) -> None:
    """Initialize the instance with the node it handles events for.

    Args:
        character (EffectCharacter | world.Body): The character or body for which the EventHandler is handling
            events.

    """
    # Typed as a character: a body's handler only takes the node actions, which bodies have too.
    self.character: EffectCharacter = typing.cast("EffectCharacter", character)
    self.registered_events: dict[
        tuple[EventHandler.Event, animation.Scene | motion.Waypoint | motion.Path | world.Body],
        list[
            tuple[
                EventHandler.Action,
                animation.Scene
                | motion.Waypoint
                | motion.Path
                | world.Body
                | int
                | Coord
                | Vec3
                | EventHandler.Callback
                | str
                | None,
            ]
        ],
    ] = {}

register_event(event, caller, action, target=None)

register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.ACTIVATE_SCENE],
    target: animation.Scene | str,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.DEACTIVATE_SCENE],
    target: animation.Scene | str | None = ...,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.ACTIVATE_PATH],
    target: motion.Path | str,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.DEACTIVATE_PATH],
    target: motion.Path | str | None = ...,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.SET_COORDINATE],
    target: Coord | Vec3,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.SET_LAYER],
    target: int,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.CALLBACK],
    target: Callback,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.RESET_APPEARANCE],
    target: None = None,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.ATTACH],
    target: world.Body,
) -> None
register_event(
    event: Event,
    caller: animation.Scene
    | motion.Waypoint
    | motion.Path
    | world.Body
    | str,
    action: typing.Literal[Action.DETACH],
    target: None = None,
) -> None

Register an event to be handled by the EventHandler.

Note: Action.RESET_APPEARANCE does not accept a target. For PATH_ACTIVATED, PATH_COMPLETE, SCENE_ACTIVATED, and SCENE_COMPLETE, caller may be the triggering object itself or its registered ID string. PATH_HOLDING and segment events require their corresponding object instances. For ACTIVATE_PATH, DEACTIVATE_PATH, ACTIVATE_SCENE, and DEACTIVATE_SCENE, string targets are resolved from registered path or scene IDs. DEACTIVATE_PATH, DEACTIVATE_SCENE, and RESET_APPEARANCE may use None where supported by the action. ATTACHED and DETACHED take the parent body as the caller.

Parameters:

Name Type Description Default
event Event

The event to register.

required
caller Scene | Waypoint | Path | Body | str

The object that triggers the event.

required
action Action

The action to take when the event is triggered.

required
target Scene | Path | Body | int | Coord | Vec3 | Callback | str | None

The target of the action.

None

Raises:

Type Description
EventRegistrationCallerError

If the caller object is not the required type for the specified event.

EventRegistrationTargetError

If the target is not the correct type for the action or if Action.RESET_APPEARANCE is provided with a target.

DuplicateEventRegistrationError

If the exact same event-caller-action-target combination has already been registered.

Example

Register an event to activate a scene when a Path is complete: event_handler.register_event(EventHandler.Event.PATH_COMPLETE, some_path, EventHandler.Action.ACTIVATE_SCENE, some_scene)

Source code in terminaltexteffects/engine/base_character.py
def register_event(
    self,
    event: Event,
    caller: animation.Scene | motion.Waypoint | motion.Path | world.Body | str,
    action: Action,
    target: animation.Scene | motion.Path | world.Body | int | Coord | Vec3 | Callback | str | None = None,
) -> None:
    """Register an event to be handled by the EventHandler.

    Note: Action.RESET_APPEARANCE does not accept a target.
    For `PATH_ACTIVATED`, `PATH_COMPLETE`, `SCENE_ACTIVATED`, and `SCENE_COMPLETE`,
    `caller` may be the triggering object itself or its registered ID string.
    `PATH_HOLDING` and segment events require their corresponding object instances.
    For `ACTIVATE_PATH`, `DEACTIVATE_PATH`, `ACTIVATE_SCENE`, and `DEACTIVATE_SCENE`,
    string targets are resolved from registered path or scene IDs. `DEACTIVATE_PATH`,
    `DEACTIVATE_SCENE`, and `RESET_APPEARANCE` may use `None` where supported by the action.
    `ATTACHED` and `DETACHED` take the parent body as the caller.

    Args:
        event (Event): The event to register.
        caller (animation.Scene | motion.Waypoint | motion.Path | world.Body | str): The object
            that triggers the event.
        action (Action): The action to take when the event is triggered.
        target (animation.Scene | motion.Path | world.Body | int | Coord | Vec3 | Callback | str | None):
            The target of the action.

    Raises:
        EventRegistrationCallerError: If the caller object is not the required type for the
            specified event.
        EventRegistrationTargetError: If the target is not the correct type for the action or if
            ``Action.RESET_APPEARANCE`` is provided with a target.
        DuplicateEventRegistrationError: If the exact same event-caller-action-target combination
            has already been registered.

    Example:
        Register an event to activate a scene when a Path is complete:
        `event_handler.register_event(EventHandler.Event.PATH_COMPLETE, some_path,
            EventHandler.Action.ACTIVATE_SCENE, some_scene)`

    """
    if isinstance(self.character, world.Body) and (event in _CHARACTER_EVENTS or action not in _NODE_ACTIONS):
        raise EventRegistrationNodeError(event, action)

    # find caller Path when provided path_id
    if event in (EventHandler.Event.PATH_ACTIVATED, EventHandler.Event.PATH_COMPLETE) and isinstance(caller, str):
        if (path_query_result := self.character.motion.query_path(caller)) is None:
            raise PathNotFoundError(path_id=caller)
        caller = path_query_result

    # find caller Scene when provided scene_id
    elif event in (EventHandler.Event.SCENE_ACTIVATED, EventHandler.Event.SCENE_COMPLETE) and isinstance(
        caller,
        str,
    ):
        if (scene_query_result := self.character.animation.query_scene(caller)) is None:
            raise SceneNotFoundError(scene_id=caller)
        caller = scene_query_result

    if _EVENT_CALLER_TYPES[event] != caller.__class__:
        raise EventRegistrationCallerError(event, caller, _EVENT_CALLER_TYPES[event])

    # find target Path when provided path_id
    if action in (EventHandler.Action.ACTIVATE_PATH, EventHandler.Action.DEACTIVATE_PATH) and isinstance(
        target,
        str,
    ):
        if (path_query_result := self.character.motion.query_path(target)) is None:
            raise PathNotFoundError(path_id=target)
        target = path_query_result

    # find target Scene when provided scene_id
    elif action in (EventHandler.Action.ACTIVATE_SCENE, EventHandler.Action.DEACTIVATE_SCENE) and isinstance(
        target,
        str,
    ):
        if (scene_query_result := self.character.animation.query_scene(target)) is None:
            raise SceneNotFoundError(scene_id=target)
        target = scene_query_result

    elif (
        action in (EventHandler.Action.DEACTIVATE_PATH, EventHandler.Action.DEACTIVATE_SCENE) and target is None
    ) or (action is EventHandler.Action.SET_COORDINATE and type(target) is Vec3):
        pass

    elif action is EventHandler.Action.DETACH:
        if target is not None:
            raise EventRegistrationTargetError(action, target, type(None))

    elif (action is EventHandler.Action.RESET_APPEARANCE and target is not None) or (
        _ACTION_TARGET_TYPES[action] != target.__class__
    ):
        raise EventRegistrationTargetError(action, target, _ACTION_TARGET_TYPES[action])

    assert isinstance(caller, (motion.Path, animation.Scene, motion.Waypoint, world.Body))
    new_event = (event, caller)
    new_action = (action, target)

    if new_event not in self.registered_events:
        self.registered_events[new_event] = []

    # Check for duplicate event-action-target combination
    if new_action in self.registered_events[new_event]:
        raise DuplicateEventRegistrationError(event, caller, action, target)

    self.registered_events[new_event].append(new_action)