Skip to main content

Drag & Drop Extension for Kivy

Project description

Draggable


Youtube
README(Japanese)

Inspired by:

  • drag_n_drop (Draggable is based on this, so please read its documentation first to get the basic idea of this one)
  • Flutter

This flower adds a drag and drop functionality to layouts and widgets. There are 3 main components used to have drag and drop:

  • The KXDraggableBehavior. An equivalent of drag_n_drop's DraggableObjectBehavior.
  • The KXReorderableBehavior. An equivalent of drag_n_drop's DraggableLayoutBehavior.
  • The KXDroppableBehavior. An equivalent of Flutter's DragTarget.

From now on, I use the term droppable to refer both KXReorderableBehavior and KXDroppableBehavior, and use the term draggable to refer KXDraggableBehavior.

Installation

It's recommended to pin the minor version, because if it changed, it means some important breaking changes occurred.

poetry add kivy_garden.draggable@~0.2
pip install "kivy_garden.draggable>=0.2,<0.3"

Main differences from drag_n_drop

  • Drag is triggered by a long-press. More precisely, when a finger of the user dropped inside a draggable, if the finger stays for draggable.drag_timeout milli seconds without traveling more than draggable.drag_distance pixels, it will be recognized as a dragging gesture.
  • Droppables can handle multiple drags simultaneously.
  • Drag can be canceled by calling draggable.drag_cancel().
  • Nested KXReorderableBehavior is not officially supported. It may or may not work depending on how drag_classes and drag_cls are set.

Flow

Once a drag has started, it will go through the following path.

stateDiagram-v2
    state cancelled? <<choice>>
    state on_a_droppable? <<choice>>
    state listed? <<choice>>
    state accepted? <<choice>>

    [*] --> on_drag_start
    on_drag_start --> cancelled?
    cancelled? --> on_a_droppable?: User lifted their finger up
    cancelled? --> on_drag_cancel: 'draggable.cancel()' was called before the user lifts their finger up

    on_a_droppable? --> listed?: Finger was on a droppable
    on_a_droppable? --> on_drag_fail: not on a droppable

    droppable_is_set: 'ctx.droppable' is set to the droppable
    listed? --> droppable_is_set: 'draggable.drag_cls' was listed in the 'droppable.drag_classes'
    listed? --> on_drag_fail: not listed

    droppable_is_set --> accepted?
    accepted? --> on_drag_succeed: Droppable accepted the drag ('droppable.accepts_drag()' returned True.)
    accepted? --> on_drag_fail

    on_drag_cancel --> on_drag_end
    on_drag_fail --> on_drag_end
    on_drag_succeed --> on_drag_end

    on_drag_end --> [*]

Cancellation

When your app switches a scene, you may want to cancel all the ongoing drags. ongoing_drags() and draggable.drag_cancel() are what you want.

from kivy_garden.draggable import ongoing_drags

def cancel_all_ongoing_drags():
    for draggable in ongoing_drags():
        draggable.drag_cancel()

Using other widgets as an emitter

Let's say you are creating a card game, and there is a deck on the screen. Say, you want the deck to emit a card when the user drops a finger on it, and want the card to follow the finger until the user lifts it up. In this situation, a widget that triggers a drag and a widget that is dragged are different. You can implement it as follows:

class Card(KXDraggableBehavior, Widget):
    pass


class Deck(Widget):
    def on_touch_down(self, touch):
        if self.collide_point(*touch.opos):
            Card(...).start_dragging_from_others_touch(self, touch)

Customization

What draggables do on_drag_succeed / on_drag_fail / on_drag_cancel are completely customizable. For example, by default, when a drag fails, the draggable will go back to where it came from with little animation. This is because the default handler of on_drag_fail is implemented as follows:

class KXDraggableBehavior:
    async def on_drag_fail(self, touch, ctx):
        await ak.animate(
            self, duration=.1,
            x=ctx.original_pos_win[0],
            y=ctx.original_pos_win[1],
        )
        restore_widget_state(self, ctx.original_state)

If you don't need the animation, and want the draggable to go back instantly, overwrite the handler as follows:

class MyDraggable(KXDraggableBehavior, Widget):
    def on_drag_fail(self, touch, ctx):
        restore_widget_state(self, ctx.original_state)

Or if you want the draggable to not go back, and want it to stay the current position, overwrite the handler as follows:

class MyDraggable(KXDraggableBehavior, Widget):
    def on_drag_fail(self, touch, ctx):
        pass

Another example: when a drag succeed, the draggable will become a child of droppable, by default. If you don't like it, and want the draggable to fade-out, overwrite the handler as follows:

class MyDraggable(KXDraggableBehavior, Widget):
    async def on_drag_succeed(self, touch, ctx):
        import asynckivy
        await asynckivy.animate(self, opacity=0)
        self.parent.remove_widget(self)

Just like that, you have free rein to change those behaviors. But note that only the default handler of on_drag_succeed and on_drag_fail can be an async function. Those two only.

You might say "What's the point of implementing a default handler as an async function, when you can just launch any number of tasks from a regular function by using asynckivy.start()?". Well, if you use asynckivy.start(), that task will run independently from the dragging process, which means the draggable might fire on_drag_end and might start another drag while the task is still running. If a default handler is an async function, its code will be a part of dragging process and is guaranteed to be finished before on_drag_end gets fired.

License

This software is released under the terms of the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kivy_garden_draggable-0.2.0.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

kivy_garden_draggable-0.2.0-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file kivy_garden_draggable-0.2.0.tar.gz.

File metadata

  • Download URL: kivy_garden_draggable-0.2.0.tar.gz
  • Upload date:
  • Size: 10.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.5.1 CPython/3.11.4 Linux/5.4.0-156-generic

File hashes

Hashes for kivy_garden_draggable-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e43be691c204dc8b09da34441a919c3a7de7b623cdfa0e727840dbff8efcb976
MD5 4e44fe99c608f5b4bc5594624f1b738a
BLAKE2b-256 53efe9077c4c0ad3f941dfc26706afbc494d444eacfcd4fc012f58adb7d5a5c4

See more details on using hashes here.

File details

Details for the file kivy_garden_draggable-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for kivy_garden_draggable-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d3d7fc715dcb7da7b8d3c4a4f64d7e4ac0b6cb1c62b467875e5fafe1170610f7
MD5 941a11639d4007ddcb25fb3367ccd41e
BLAKE2b-256 507de5309612b42728c31e3933a40907114c18fbe325a5c29f5df40727754181

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page