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
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.
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) ) )
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_ended. It’s also possible
to extend the gesture by reimplementing its class.
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))
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.
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) ) )