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.
kivy_mainline_and_py3.8
Tyler Goodlet 2018-12-01 18:39:01 -05:00
parent 0fbab8b831
commit 5c070b1c43
1 changed files with 32 additions and 21 deletions

View File

@ -5,7 +5,6 @@ __author__ = 'Olivier Poyen'
from kivy.properties import BooleanProperty, ObjectProperty from kivy.properties import BooleanProperty, ObjectProperty
from kivy.factory import Factory
from kivy.core.window import Window 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 # be used in `on_enter` or `on_leave` in order to know where was dispatched
# the event. # the event.
border_point = ObjectProperty(None) border_point = ObjectProperty(None)
_widgets = []
_last_hovered = None
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.register_event_type('on_enter') self.register_event_type('on_enter')
self.register_event_type('on_leave') self.register_event_type('on_leave')
Window.bind(mouse_pos=self.on_mouse_pos) HoverBehavior._widgets.append(self)
super(HoverBehavior, self).__init__(**kwargs) 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 # don't proceed if I'm not displayed <=> If have no parent
if not self.get_root_window(): # if not self.get_root_window():
return # return
pos = args[1] pos = args[1]
# Next line to_widget allow to compensate for relative layout # Next line to_widget allow to compensate for relative layout
inside = self.collide_point(*self.to_widget(*pos)) for widget in cls._widgets:
if self.hovered == inside: w_coords = widget.to_widget(*pos)
# We have already done what was needed 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 return
self.border_point = pos
self.hovered = inside
if inside:
self.dispatch('on_enter')
else:
self.dispatch('on_leave')
# implement these in the widget impl # implement these in the widget impl
def on_enter(self): @classmethod
def on_enter(cls):
pass pass
def on_leave(self): @classmethod
def on_leave(cls):
pass pass
# register for global use via kivy.factory.Factory
Factory.register('HoverBehavior', HoverBehavior)