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 reading its documentation may help you understand Draggable)
  • 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

Pin the minor version.

pip install "kivy_garden.draggable>=0.3,<0.4"

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 --> [*]

Important Change

Starting with version 0.3, on_drag_cancel may occur after on_drag_fail or on_drag_succeed if their default handler is an async function.

Cancellation

When your app switches a scene, you may want to cancel all 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.anim_attrs(
            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.anim_attrs(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 async functions from a regular function by using asynckivy.managed_start()?". Well, if you use asynckivy.managed_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.3.0.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kivy_garden_draggable-0.3.0-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: kivy_garden_draggable-0.3.0.tar.gz
  • Upload date:
  • Size: 8.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.4 CPython/3.11.4 Linux/5.4.0-216-generic

File hashes

Hashes for kivy_garden_draggable-0.3.0.tar.gz
Algorithm Hash digest
SHA256 7666ad7484e0b915a5d790f94d93ee47577050d71109109e92e48c4675b7c963
MD5 5635b7cde555cfc8e2ef019441920d56
BLAKE2b-256 edf786e7c5d6ee1bc603f191931d77ce687c13ede6003a95d4a70c4c60e84ac0

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for kivy_garden_draggable-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 398de7ba718038a6b1a20b9e4b9c8ec90e60190f84f47eae97676e3a76205133
MD5 c19ac3bc63481a768f2366e30dbf76a3
BLAKE2b-256 dfa03988ad9a518be38aeb369990427ff40648db3415955cfac8f0f714097520

See more details on using hashes here.

Supported by

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