From 5c070b1c434e7e31fbb822289e2d984ebf137a98 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Sat, 1 Dec 2018 18:39:01 -0500 Subject: [PATCH] Faster highlighting via single loop and callback Thanks yet again to @tshirtman for suggesting this. Instead of defining a `on_mouse_pos()` on every widget simply register and track each widget and loop through them all once (or as much as is necessary) in a single callback. The assumption here is that we get a performance boost by looping widgets instead of having `kivy` loop and call back each widget thus avoiding costly python function calls. --- piker/ui/kivy/hoverable.py | 53 +++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/piker/ui/kivy/hoverable.py b/piker/ui/kivy/hoverable.py index 4aa70c24..fda4452f 100644 --- a/piker/ui/kivy/hoverable.py +++ b/piker/ui/kivy/hoverable.py @@ -5,7 +5,6 @@ __author__ = 'Olivier Poyen' from kivy.properties import BooleanProperty, ObjectProperty -from kivy.factory import Factory from kivy.core.window import Window @@ -23,38 +22,50 @@ class HoverBehavior(object): # be used in `on_enter` or `on_leave` in order to know where was dispatched # the event. border_point = ObjectProperty(None) + _widgets = [] + _last_hovered = None def __init__(self, **kwargs): self.register_event_type('on_enter') self.register_event_type('on_leave') - Window.bind(mouse_pos=self.on_mouse_pos) + HoverBehavior._widgets.append(self) super(HoverBehavior, self).__init__(**kwargs) + Window.bind(mouse_pos=self.on_mouse_pos) - def on_mouse_pos(self, *args): + @classmethod + def on_mouse_pos(cls, *args): + # XXX: how to still do this at the class level? # don't proceed if I'm not displayed <=> If have no parent - if not self.get_root_window(): - return + # if not self.get_root_window(): + # return + pos = args[1] # Next line to_widget allow to compensate for relative layout - inside = self.collide_point(*self.to_widget(*pos)) - if self.hovered == inside: - # We have already done what was needed - return - self.border_point = pos - self.hovered = inside - if inside: - self.dispatch('on_enter') - else: - self.dispatch('on_leave') + for widget in cls._widgets: + w_coords = widget.to_widget(*pos) + inside = widget.collide_point(*w_coords) + if inside and widget.hovered: + return + elif inside: + # un-highlight the last highlighted + last_hovered = cls._last_hovered + if last_hovered: + last_hovered.dispatch('on_leave') + last_hovered.hovered = False + + # highlight new widget + widget.border_point = pos + widget.hovered = True + widget.dispatch('on_enter') + cls._last_hovered = widget + return # implement these in the widget impl - def on_enter(self): + @classmethod + def on_enter(cls): pass - def on_leave(self): + @classmethod + def on_leave(cls): pass - - -# register for global use via kivy.factory.Factory -Factory.register('HoverBehavior', HoverBehavior)