Gestures#

Gestures handle all of the logic needed to process user-input events such as click and drag and recognize when those events happen on the shape. When recognizing it, SceneUI runs a callback to update the state of a view or perform an action.

Add Gesture Callback#

Each gesture applies to a specific shape in the scene hierarchy. To recognize a gesture event on a particular shape, it’s necessary to create and configure the gesture object. SceneUI provides the full state of the gesture to the callback using the gesture_payload object. The gesture_payload object contains the coordinates of the itersection of mouse pointer and the shape in world and shape spaces, the distance between last itersection and parametric coordinates. It’s effortless to modify the state of the shape using this data.

Code Result

def move(transform: sc.Transform, shape: sc.AbstractShape):
    """Called by the gesture"""
    translate = shape.gesture_payload.moved
    # Move transform to the direction mouse moved
    current = sc.Matrix44.get_translation_matrix(*translate)
    transform.transform *= current

with scene_view.scene:
    transform = sc.Transform()
    with transform:
        sc.Line(
            [-1, 0, 0],
            [1, 0, 0],
            color=cl.blue,
            thickness=5,
            gesture=sc.DragGesture(
                on_changed_fn=partial(move, transform)
            )
        )

Reimplementing Gesture#

Some gestures can receive the update in a different state. For example, DragGesture offers the update when the user presses the mouse, moves the mouse, and releases the mouse: on_began, on_changed, on_ended. It’s also possible to extend the gesture by reimplementing its class.

Code Result

class Move(sc.DragGesture):
    def __init__(self, transform: sc.Transform):
        super().__init__()
        self.__transform = transform

    def on_began(self):
        self.sender.color = cl.red

    def on_changed(self):
        translate = self.sender.gesture_payload.moved
        # Move transform to the direction mouse moved
        current = sc.Matrix44.get_translation_matrix(*translate)
        self.__transform.transform *= current

    def on_ended(self):
        self.sender.color = cl.blue

with scene_view.scene:
    transform = sc.Transform()
    with transform:
        sc.Rectangle(color=cl.blue, gesture=Move(transform))

Gesture Manager#

Gestures track incoming input events separately, but it’s normally necessary to let only one gesture be executed because it prevents user input from triggering more than one action at a time. For example, if multiple shapes are under the mouse pointer, normally, the only gesture of the closest shape should be processed. However, this default behavior can introduce unintended side effects. To solve those problems, it’s necessary to use the gesture manager.

GestureManager controls the priority of gestures if they are processed at the same time. It prioritizes the desired gestures and prevents unintended gestures from being executed.

In the following example, the gesture of the red rectangle always wins even if both rectangles overlap.

Code Result

class Manager(sc.GestureManager):
    def __init__(self):
        super().__init__()

    def should_prevent(self, gesture, preventer):
        # prime gesture always wins
        if preventer.name == "prime":
            return True

def move(transform: sc.Transform, shape: sc.AbstractShape):
    """Called by the gesture"""
    translate = shape.gesture_payload.moved
    current = sc.Matrix44.get_translation_matrix(*translate)
    transform.transform *= current

mgr = Manager()

with scene_view.scene:
    transform1 = sc.Transform()
    with transform1:
        sc.Rectangle(
            color=cl.red,
            gesture=sc.DragGesture(
                name="prime",
                manager=mgr,
                on_changed_fn=partial(move, transform1)
            )
        )
    transform2 = sc.Transform(
        transform=sc.Matrix44.get_translation_matrix(2, 0, 0)
    )
    with transform2:
        sc.Rectangle(
            color=cl.blue,
            gesture=sc.DragGesture(
                manager=mgr,
                on_changed_fn=partial(move, transform2)
            )
        )