2

Assume I have this UI adapted from the ScrollView example:

from kivy.app import App
from kivy.uix.slider import Slider
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout

class ScrollViewApp(App):
    def build(self):
        layout = GridLayout(cols=1, padding=10, spacing=10,
                size_hint=(None, None), width=500)
        layout.bind(minimum_height=layout.setter('height'))
        for i in range(15):
            blt = BoxLayout(size_hint=(None, None), size=(480, 40))
            blt.add_widget(Label(text="Slider %d" % i))
            btn = Slider(value=i, min=0, max=42, size=(400, 40),
                         size_hint=(None, None))
            blt.add_widget(btn)
            layout.add_widget(blt)
        scroll = ScrollView(size_hint=(None, None), size=(500, 320),
                pos_hint={'center_x': .5, 'center_y': .5}, do_scroll_x=False)
        scroll.add_widget(layout)
        return scroll

if __name__ == '__main__':
    ScrollViewApp().run()

Due to scroll_timeout, interactions with the Sliders are delayed. Is it possible to define areas in the ScrollView in which touch events are just passed through to children without the delay (and without initiating a scroll)?

zeeMonkeez
  • 5,057
  • 3
  • 33
  • 56

2 Answers2

2

Have a look at Widget touch event bubbling.

I've never had to do the same thing as you but maybe you could create a custom class that inherits ScrollView and override on_touch_down event where you could:

  1. Disable scroll
  2. Call super.on_touch_down
  3. Enable scroll.

Another way might be creating a custom widget that inherits Slider class that the user clicks. Then overload it's on_touch_down method with return True. Documentation says:

In order to stop this event bubbling, one of these methods must return True

ScrollView also fires on_scroll_start event so maybe you could do something similar there.

martin
  • 93,354
  • 25
  • 191
  • 226
  • Thanks for your answer, @Martin. I knew I had to tamper with `on_touch_down`, but didn't quite know how. Seems like I found a way (which is different from your suggestion). – zeeMonkeez Mar 01 '16 at 14:35
2

I came up with a simple override of ScrollView's on_touch_down, which so far seems to satisfy my needs. The idea is to test whether a touch event falls into an 'exclusion zone' (in the example below only the x dimension is checked, but it'd be trivial to extend this to arbitrary rectangular areas). If it does fall into that exclusion zone, the on_touch_down event will be dispatched to the ScrollView's children. If a child captures it, the event is swallowed. In all other cases, super.on_touch_down will be called, i.e. normal scroll behaviour is initiated. That has the benefit of still being able to scroll if the touch does not land on a Slider (in the question's example).

class MSV(ScrollView):
    x_exclusion_lower = NumericProperty(None, allownone=True)
    x_exclusion_upper = NumericProperty(None, allownone=True)
    x_exclusion = ReferenceListProperty(x_exclusion_lower, x_exclusion_upper)

    def on_touch_down(self, touch):
        pos_in_sv = self.to_local(*touch.pos)
        if (self.x_exclusion_lower is not None or self.x_exclusion_upper is not None) and (self.x_exclusion_lower is None or self.x_exclusion_lower <= pos_in_sv[0]) and \
                (self.x_exclusion_upper is None or self.x_exclusion_upper >= pos_in_sv[0]):
            touch.push()
            touch.apply_transform_2d(self.to_local)
            if self.dispatch_children('on_touch_down', touch):
                return True
            touch.pop()
        super(MSV, self).on_touch_down(touch)
zeeMonkeez
  • 5,057
  • 3
  • 33
  • 56