From 8f66e319078d234ea82dc35e83d772a9f66fc7d2 Mon Sep 17 00:00:00 2001 From: Pan Xinmiao Date: Mon, 9 Sep 2024 18:55:29 +0800 Subject: [PATCH] Prevent UI events from propagating beyond imgui (#569) * event propagation * update * fix remove event handler --- wgpu/gui/base.py | 18 ++++++++++++------ wgpu/utils/imgui/imgui_renderer.py | 27 ++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/wgpu/gui/base.py b/wgpu/gui/base.py index a9482dfa..8b49b6df 100644 --- a/wgpu/gui/base.py +++ b/wgpu/gui/base.py @@ -197,7 +197,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._last_event_time = 0 self._pending_events = {} - self._event_handlers = defaultdict(set) + self._event_handlers = defaultdict(list) def _get_event_wait_time(self): """Calculate the time to wait for the next event dispatching. @@ -274,18 +274,21 @@ def handle_event(self, event): """ # Collect callbacks event_type = event.get("event_type") - callbacks = self._event_handlers[event_type] | self._event_handlers["*"] + callbacks = self._event_handlers[event_type] + self._event_handlers["*"] # Dispatch - for callback in callbacks: + for _, callback in callbacks: with log_exception(f"Error during handling {event['event_type']} event"): + if event.get("stop_propagation", False): + break callback(event) - def add_event_handler(self, *args): + def add_event_handler(self, *args, order=0): """Register an event handler to receive events. Arguments: callback (callable): The event handler. Must accept a single event argument. *types (list of strings): A list of event types. + order (int): The order in which the handler is called. Lower numbers are called first. Default is 0. For the available events, see https://jupyter-rfb.readthedocs.io/en/stable/events.html. @@ -331,7 +334,8 @@ def my_handler(event): def decorator(_callback): for type in types: - self._event_handlers[type].add(_callback) + self._event_handlers[type].append((order, _callback)) + self._event_handlers[type].sort(key=lambda x: x[0]) return _callback if decorating: @@ -346,4 +350,6 @@ def remove_event_handler(self, callback, *types): *types (list of strings): A list of event types. """ for type in types: - self._event_handlers[type].remove(callback) + self._event_handlers[type] = [ + (o, cb) for o, cb in self._event_handlers[type] if cb is not callback + ] diff --git a/wgpu/utils/imgui/imgui_renderer.py b/wgpu/utils/imgui/imgui_renderer.py index 7d584a92..88e07aaa 100644 --- a/wgpu/utils/imgui/imgui_renderer.py +++ b/wgpu/utils/imgui/imgui_renderer.py @@ -78,11 +78,13 @@ def __init__( self._backend.io.display_framebuffer_scale = (scale, scale) canvas.add_event_handler(self._on_resize, "resize") - canvas.add_event_handler(self._on_mouse_move, "pointer_move") - canvas.add_event_handler(self._on_mouse, "pointer_up", "pointer_down") - canvas.add_event_handler(self._on_key, "key_up", "key_down") - canvas.add_event_handler(self._on_wheel, "wheel") - canvas.add_event_handler(self._on_char_input, "char") + canvas.add_event_handler(self._on_mouse_move, "pointer_move", order=-99) + canvas.add_event_handler( + self._on_mouse, "pointer_up", "pointer_down", order=-99 + ) + canvas.add_event_handler(self._on_key, "key_up", "key_down", order=-99) + canvas.add_event_handler(self._on_wheel, "wheel", order=-99) + canvas.add_event_handler(self._on_char_input, "char", order=-99) self._update_gui_function = None @@ -152,11 +154,17 @@ def _on_resize(self, event): def _on_mouse_move(self, event): self._backend.io.add_mouse_pos_event(event["x"], event["y"]) + if self._backend.io.want_capture_mouse: + event["stop_propagation"] = True + def _on_mouse(self, event): event_type = event["event_type"] down = event_type == "pointer_down" self._backend.io.add_mouse_button_event(event["button"] - 1, down) + if self._backend.io.want_capture_mouse: + event["stop_propagation"] = True + def _on_key(self, event): event_type = event["event_type"] down = event_type == "key_down" @@ -182,8 +190,17 @@ def _on_key(self, event): key = self.KEY_MAP_MOD[key_name] self._backend.io.add_key_event(key, down) + if self._backend.io.want_capture_keyboard: + event["stop_propagation"] = True + def _on_wheel(self, event): self._backend.io.add_mouse_wheel_event(event["dx"] / 100, event["dy"] / 100) + if self._backend.io.want_capture_mouse: + event["stop_propagation"] = True + def _on_char_input(self, event): self._backend.io.add_input_characters_utf8(event["char_str"]) + + if self._backend.io.want_text_input: + event["stop_propagation"] = True