Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to switch Deck and Card area #34

Closed
LukasSliacky opened this issue Jul 17, 2024 · 4 comments
Closed

How to switch Deck and Card area #34

LukasSliacky opened this issue Jul 17, 2024 · 4 comments

Comments

@LukasSliacky
Copy link

Sorry for my post, i think it isn't bug, but i don't know how to modify your example using_other_widget_as_an_emitter.py to switch sides of application with keep functionality.

In your example you have Decks on the right side and Cards on the left side. I need to switch it vice versa: I need Decks on the left Side and Cards on the right side

When i tried it only move block in kv code, UI looks fine, but draggable functionality isn't works and dragged card will take back to the Deck. Probably, I have problem with some ID's or something similar but on with my skill level cann't modify your example.

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', ]
    size_hint: None, None
    size: 120, 120
    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:
    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)

    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.
    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
'''


class Cell(KXDroppableBehavior, FloatLayout):
    def accepts_drag(self, touch, ctx, draggable) -> bool:
        print('>>>>>>>>', self.children)
        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):
            cell = Cell()
            board.add_widget(cell)


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

Please, can you help me with this modification?

Thank you

@gottadiveintopython
Copy link
Member

gottadiveintopython commented Jul 20, 2024

OK, I confirmed the issue, and I think this is a bug in the draggable (or Kivy). However, the difference between the original example and yours isn't just where the decks are placed. You placed the board inside a ScrollView instead of a RelativeLayout, and if you revert that part, the issue will disappear.

That doesn't change my assumption that this is a bug in the library, as it should work with ScrollView too, but you need to be careful about what you changed from the original.

I'll investigate this further.

@gottadiveintopython
Copy link
Member

gottadiveintopython commented Aug 19, 2024

Okay "the side" does matter because this issue occurs only when the ScrollView (the parent of the board) receives a touch event before the decks. (Widgets added to a layout later will receive touch events earlier)

The real solution should be to fix the ScrollView, but that would require quite a bit of work. If you are not willing to do that, here's a hacky workaround.

BoxLayout:
    BoxLayout:
        Deck:
            board: board
            text: 'numbers'
            on_touch_down:
                touch = args[1]
                touch.ud.pop(board_sv._get_uid('svavoid')) if self.collide_point(*touch.opos) else None
        Deck:
            board: board
            text: 'letters'
            on_touch_down:
                touch = args[1]
                touch.ud.pop(board_sv._get_uid('svavoid')) if self.collide_point(*touch.opos) else None
    Widget:
        size_hint_x: .1
    ScrollView:
        id: board_sv
        GridLayout:
            id: board

Once a ScrollView sets a svavoid flag on a touch, it will no longer dispatch that touch to its child (board in this case), which is why the board doesn't respond to drag-and-drop actions. The code above works around this by unsetting the flag.

Another workaround is to use GridLayout and set its orientation property to rt-tb or rl-bt. This still is hacky, and is less flexible than the above, but in return, you don't need to touch the svavoid flag.

GridLayout:
    orientation: 'rl-tb'
    rows: 1
    ScrollView:
        id: board_sv
        GridLayout:
            id: board
    Widget:
        size_hint_x: .1
    BoxLayout:
        Deck:
            board: board
            text: 'numbers'
        Deck:
            board: board
            text: 'letters'

@gottadiveintopython
Copy link
Member

@LukasSliacky no reply?

@LukasSliacky
Copy link
Author

Wow, really thank you for your support. In my case are both recommended solutions acceptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants