Search code examples
pythonkivydraggablekivy-language

Kivy - How can I modify example for using ScrollView + GridLayout (Kivy-Garden Draggable)?


This Kivy - Draggable example works great:

from kivy.properties import ObjectProperty
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout

from kivy_garden.draggable import KXDroppableBehavior, KXDraggableBehavior

KV_CODE = '''
#:import ascii_uppercase string.ascii_uppercase

<Cell>:
    drag_classes: ['card', ]
    canvas.before:
        Color:
            rgba: .1, .1, .1, 1
        Rectangle:
            pos: self.pos
            size: self.size
<Card>:
    drag_cls: 'card'
    drag_timeout: 0
    font_size: 100
    opacity: .3 if self.is_being_dragged else 1.
    canvas.after:
        Color:
            rgba: 1, 1, 1, 1
        Line:
            rectangle: [*self.pos, *self.size, ]
<Deck>:
    canvas.after:
        Color:
            rgba: 1, 1, 1, 1
        Line:
            rectangle: [*self.pos, *self.size, ]

BoxLayout:
    Widget:
        size_hint_x: .1

    # Put the board inside a RelativeLayout just to confirm the coordinates are properly transformed.
    # This is not necessary for this example to work.
    RelativeLayout:
        GridLayout:
            id: board
            cols: 4
            rows: 4
            spacing: 10
            padding: 10

    BoxLayout:
        orientation: 'vertical'
        size_hint_x: .2
        padding: '20dp', '40dp'
        spacing: '80dp'
        RelativeLayout:  # Put a deck inside a RelativeLayout just ...
            Deck:
                board: board
                text: 'numbers'
                font_size: '20sp'
                text_iter: (str(i) for i in range(10))
        Deck:
            board: board
            text: 'letters'
            font_size: '20sp'
            text_iter: iter(ascii_uppercase)
'''


class Cell(KXDroppableBehavior, FloatLayout):
    def accepts_drag(self, touch, ctx, draggable) -> bool:
        return not self.children

    def add_widget(self, widget, *args, **kwargs):
        widget.size_hint = (1, 1, )
        widget.pos_hint = {'x': 0, 'y': 0, }
        return super().add_widget(widget, *args, **kwargs)


class Card(KXDraggableBehavior, Label):
    pass


class Deck(Label):
    text_iter = ObjectProperty()
    board = ObjectProperty()

    def on_touch_down(self, touch):
        if self.collide_point(*touch.opos):
            if (text := next(self.text_iter, None)) is not None:
                card = Card(text=text, size=self._get_cell_size())
                card.start_dragging_from_others_touch(self, touch)
            return True

    def _get_cell_size(self):
        return self.board.children[0].size


class SampleApp(App):
    def build(self):
        return Builder.load_string(KV_CODE)

    def on_start(self):
        board = self.root.ids.board
        for __ in range(board.cols * board.rows):
            board.add_widget(Cell())


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

and I need to modify this app for using ScrollView in "droppable" zone (as you can see on screenshot) but when only change RelativeLayout to ScrollView, ScrollView not works.

In my "experiments" i have only two states: ScrollView works, but it isnt works droppable functionality or droppable functiononality works, but it isnt possible to scroll cards.

Please, how can i customize this examlpe?

Draggable + ScrollView


Solution

  • You must follow the directions as described in the ScrollView documentation. In order for the ScrollView to work, its child must have its size specified (typically using minimum_size or minimum_height). Try changing the scrollable area to:

    # RelativeLayout:
    ScrollView:
        size_hint_x: .7
        do_scroll_x: False
        GridLayout:
            id: board
            size_hint_y: None
            height: self.minimum_height
            cols: 4
            rows: 40
            spacing: 10
            padding: 10
    

    In order for the minimum_height to work, the sizes of the children of the GridLayout must have well defined sizes. This requires some changes to the Cell definition:

    <Cell>:
        drag_classes: ['card', ]
        size_hint: None, None
        size: 50, 80
        canvas.before:
            Color:
                rgba: .1, .1, .1, 1
            Rectangle:
                pos: self.pos
                size: self.size
    

    with these changes, I think your code will work.