MDCardSwipe - Swiping stops when a touch moves onto other widgets

24 views
Skip to first unread message

大橋和季

unread,
Jun 29, 2024, 11:00:03 PM (3 days ago) Jun 29
to Kivy users support
I've got into a practice of MDCardSwipe. My goal is creating an app that has swiping cards like Gmail for smart phones.

The problem I've got is when a touch to swipe a card moves onto other widgets, swiping stops.

To solve it, I tried this.

from kivy.lang import Builder
from kivy.properties import StringProperty

from kivymd.app import MDApp
from kivymd.uix.card import MDCardSwipe
from kivymd.uix.list import MDList

KV = '''
<SwipeToDeleteItem>:
    size_hint_y: None
    height: content.height
    type_swipe: "auto"
    # anchor: "right"
    max_swipe_x: .5
    on_swipe_complete: app.remove_item(self)

    MDCardSwipeLayerBox:
        # Content under the card.

    MDCardSwipeFrontBox:
        ripple_behavior: True

        # Content of card.
        OneLineListItem:
            id: content
            text: root.text
            _no_ripple_effect: True
            on_press: print(self.text)

<MyMDList>:
    spacing: '5dp'

MDScreen:

    MDBoxLayout:
        orientation: "vertical"
        spacing: "10dp"

        MDTopAppBar:
            elevation: 2
            title: "MDCardSwipe"

        ScrollView:
            scroll_timeout : 100

            MyMDList:
                id: my_md_list

                canvas.before:
                    Color:
                        rgba: .1, .2, .3, 1
                    Rectangle:
                        pos: self.pos
                        size: self.size

        MDBottomAppBar:
            elevation: 2
'''

class SwipeToDeleteItem(MDCardSwipe):
    '''Card with `swipe-to-delete` behavior.'''
    text = StringProperty()

    def on_touch_move(self, touch):
        self._distance += touch.dx
        expr = False

        if self.anchor == "left" and touch.dx >= 0:
            expr = abs(self._distance) < self.swipe_distance
        elif self.anchor == "right" and touch.dx < 0:
            expr = abs(self._distance) > self.swipe_distance

        if expr and not self._opens_process:
            self._opens_process = True
            self._to_closed = False
        if self._opens_process:
            self.open_progress = max(
                min(self.open_progress + touch.dx / self.width, 2.5), 0
            )
        return False

    def on_touch_up(self, touch):
        self._distance = 0
        if not self._to_closed:
            self._opens_process = False
            self.complete_swipe()
        return False

class MyMDList(MDList):
    is_dispatched = None
    touchable = True
   
    def on_touch_down(self, touch):
        self.touchable = True
        self.is_dispatched = [c.dispatch('on_touch_down', touch) for c in self.children]
        self.touchable = False
        return any(self.is_dispatched)

    def on_touch_move(self, touch):
        self.touchable = False
        try:
            return any([c.dispatch('on_touch_move', touch) if self.is_dispatched[i] else None for i, c in enumerate(self.children)])
        except:
            return False

    def on_touch_up(self, touch):
        self.touchable = True
        try:
            return any([c.dispatch('on_touch_up', touch) if self.is_dispatched[i] else None for i, c in enumerate(self.children)])
        except:
            return False

class TestCard(MDApp):

    def build(self):
        return Builder.load_string(KV)

    def on_start(self):
        '''Creates a list of cards.'''
        self.my_md_list = self.root.ids.my_md_list
        for i in range(10):
            self.my_md_list.add_widget(
                SwipeToDeleteItem(text=f"One-line item {i}")
            )

    def remove_item(self, instance):
        if instance.state == 'opened':
            self.my_md_list.remove_widget(instance)

TestCard().run()

It looks good, but a touch goes onto MDTopAppBar, MDBottomAppBar or out of the window, swiping stops.

Next I tried this.

from kivy.lang import Builder
from kivy.properties import StringProperty

from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.card import MDCardSwipe

Builder.load_string('''
<SwipeToDeleteItem>:
    size_hint_y: None
    height: content.height
    type_swipe: "auto"
    # anchor: "right"
    max_swipe_x: .5
    on_swipe_complete: app.remove_item(self)

    MDCardSwipeLayerBox:
        # Content under the card.

    MDCardSwipeFrontBox:
        # on_press: print(root.text)
        ripple_behavior: True

        # Content of card.
        OneLineListItem:
            id: content
            text: root.text
            height: 50
            _no_ripple_effect: True
            on_press: print(self.text)

<MyMDScreen>:

    MDBoxLayout:
        orientation: "vertical"
        spacing: "10dp"

        MDTopAppBar:
            elevation: 2
            title: "MDCardSwipe"
 
        ScrollView:
            scroll_timeout : 100

            MDList:
                id: md_list

        MDBottomAppBar:
            elevation: 2
''')

class SwipeToDeleteItem(MDCardSwipe):
    '''Card with `swipe-to-delete` behavior.'''
    text = StringProperty()

    def on_touch_move(self, touch):
        self._distance += touch.dx
        expr = False

        if self.anchor == "left" and touch.dx >= 0:
            expr = abs(self._distance) < self.swipe_distance
        elif self.anchor == "right" and touch.dx < 0:
            expr = abs(self._distance) > self.swipe_distance

        if expr and not self._opens_process:
            self._opens_process = True
            self._to_closed = False
        if self._opens_process:
            self.open_progress = max(
                min(self.open_progress + touch.dx / self.width, 2.5), 0
            )
        return False

    def on_touch_up(self, touch):
        self._distance = 0
        if not self._to_closed:
            self._opens_process = False
            self.complete_swipe()
        return False

class MyMDScreen(MDScreen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.is_dispatched = None
        self.touchable = True
        self.md_list = self.ids.md_list

    def on_touch_down(self, touch):
        self.touchable = True
        self.is_dispatched = [c.dispatch('on_touch_down', touch) for c in self.md_list.children]
        self.touchable = False
        return any(self.is_dispatched)

    def on_touch_move(self, touch):
        self.touchable = False
        return any([c.dispatch('on_touch_move', touch) if self.is_dispatched[i] else None for i, c in enumerate(self.md_list.children)])

    def on_touch_up(self, touch):
        self.touchable = True
        return any([c.dispatch('on_touch_up', touch) if self.is_dispatched[i] else None for i, c in enumerate(self.md_list.children)])

class TestCard(MDApp):
    def build(self):
        my_mdscreen = MyMDScreen()
        return my_mdscreen

    def on_start(self):
        '''Creates a list of cards.'''
        self.md_list = self.root.ids.md_list
        for i in range(12):
            self.md_list.add_widget(
                SwipeToDeleteItem(text=f"One-line item {i}")
            )

    def remove_item(self, instance):
        if instance.state == 'opened':
            self.md_list.remove_widget(instance)

TestCard().run()


In this case swiping doesn't stop, but it has a problem of coordinates of touch and the ScrollView doesn't work.

How can I solve it?

ELLIOT GARBUS

unread,
Jun 30, 2024, 11:51:17 AM (2 days ago) Jun 30
to Kivy users support
I won't have  chance to run your code for a few days - but I can see an issue with the code.

In Kivy , touches are dispatched to all of the widgets.  The widget needs to check if it has been touched using the collide_point() method.

Read:
https://kivy.org/doc/stable/guide/inputs.html#touch-event-basics  also read the section on grabbing touch events

A description of how touches are sent to widgets is described here:

Let me know if you get things working.



From: kivy-...@googlegroups.com <kivy-...@googlegroups.com> on behalf of 大橋和季 <0cd3882...@gmail.com>
Sent: Saturday, June 29, 2024 8:00 PM
To: Kivy users support <kivy-...@googlegroups.com>
Subject: [kivy-users] MDCardSwipe - Swiping stops when a touch moves onto other widgets
 
--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/5f3d4b2d-a813-49f2-bee2-245bd3fae861n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages