kivy-garden-draggable


Namekivy-garden-draggable JSON
Version 0.2.0 PyPI version JSON
download
home_pagehttps://github.com/kivy-garden/draggable
SummaryDrag & Drop Extension for Kivy
upload_time2023-08-28 11:11:45
maintainer
docs_urlNone
authorNattōsai Mitō
requires_python>=3.8.1,<4.0.0
licenseMIT
keywords kivy
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Draggable

![](http://img.youtube.com/vi/CjiRZjiSqgA/0.jpg)  
[Youtube][youtube]  
[README(Japanese)](README_jp.md)  

Inspired by:

* [drag_n_drop][drag_n_drop] (`Draggable` is based on this, so please read its documentation first to get the basic idea of this one)
* [Flutter][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.

```mermaid
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.

```python
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:

```python
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:

```python
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:

```python
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:

```python
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:

```python
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.

[drag_n_drop]:https://github.com/kivy-garden/drag_n_drop
[flutter]:https://api.flutter.dev/flutter/widgets/Draggable-class.html
[youtube]:https://www.youtube.com/playlist?list=PLNdhqAjzeEGiepWKfP43Dh7IWqn3cQtpQ

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/kivy-garden/draggable",
    "name": "kivy-garden-draggable",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8.1,<4.0.0",
    "maintainer_email": "",
    "keywords": "kivy",
    "author": "Natt\u014dsai Mit\u014d",
    "author_email": "flow4re2c@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/53/ef/e9077c4c0ad3f941dfc26706afbc494d444eacfcd4fc012f58adb7d5a5c4/kivy_garden_draggable-0.2.0.tar.gz",
    "platform": null,
    "description": "# Draggable\n\n![](http://img.youtube.com/vi/CjiRZjiSqgA/0.jpg)  \n[Youtube][youtube]  \n[README(Japanese)](README_jp.md)  \n\nInspired by:\n\n* [drag_n_drop][drag_n_drop] (`Draggable` is based on this, so please read its documentation first to get the basic idea of this one)\n* [Flutter][flutter]\n\nThis flower adds a drag and drop functionality to layouts and widgets. There are 3\nmain components used to have drag and drop:\n\n- The `KXDraggableBehavior`. An equivalent of drag_n_drop's\n  `DraggableObjectBehavior`.\n- The `KXReorderableBehavior`. An equivalent of drag_n_drop's\n  `DraggableLayoutBehavior`.\n- The `KXDroppableBehavior`. An equivalent of Flutter's `DragTarget`.\n\nFrom now on, I use the term `droppable` to refer both `KXReorderableBehavior` and `KXDroppableBehavior`, and use the term `draggable` to refer `KXDraggableBehavior`.\n\n## Installation\n\nIt's recommended to pin the minor version, because if it changed, it means some important breaking changes occurred.\n\n```\npoetry add kivy_garden.draggable@~0.2\npip install \"kivy_garden.draggable>=0.2,<0.3\"\n```\n\n## Main differences from drag_n_drop\n\n- Drag is triggered by a long-press. More precisely, when a finger of the user\n  dropped inside a draggable, if the finger stays for `draggable.drag_timeout`\n  milli seconds without traveling more than `draggable.drag_distance` pixels, it will\n  be recognized as a dragging gesture.\n- Droppables can handle multiple drags simultaneously.\n- Drag can be canceled by calling `draggable.drag_cancel()`.\n- Nested `KXReorderableBehavior` is not officially supported. It may or may\n  not work depending on how `drag_classes` and `drag_cls` are set.\n\n## Flow\n\nOnce a drag has started, it will go through the following path.\n\n```mermaid\nstateDiagram-v2\n    state cancelled? <<choice>>\n    state on_a_droppable? <<choice>>\n    state listed? <<choice>>\n    state accepted? <<choice>>\n\n    [*] --> on_drag_start\n    on_drag_start --> cancelled?\n    cancelled? --> on_a_droppable?: User lifted their finger up\n    cancelled? --> on_drag_cancel: 'draggable.cancel()' was called before the user lifts their finger up\n\n    on_a_droppable? --> listed?: Finger was on a droppable\n    on_a_droppable? --> on_drag_fail: not on a droppable\n\n    droppable_is_set: 'ctx.droppable' is set to the droppable\n    listed? --> droppable_is_set: 'draggable.drag_cls' was listed in the 'droppable.drag_classes'\n    listed? --> on_drag_fail: not listed\n\n    droppable_is_set --> accepted?\n    accepted? --> on_drag_succeed: Droppable accepted the drag ('droppable.accepts_drag()' returned True.)\n    accepted? --> on_drag_fail\n\n    on_drag_cancel --> on_drag_end\n    on_drag_fail --> on_drag_end\n    on_drag_succeed --> on_drag_end\n\n    on_drag_end --> [*]\n```\n\n## Cancellation\n\nWhen your app switches a scene, you may want to cancel all the ongoing drags.\n`ongoing_drags()` and `draggable.drag_cancel()` are what you want.\n\n```python\nfrom kivy_garden.draggable import ongoing_drags\n\ndef cancel_all_ongoing_drags():\n    for draggable in ongoing_drags():\n        draggable.drag_cancel()\n```\n\n## Using other widgets as an emitter\n\nLet's say you are creating a card game, and there is a deck on the screen.\nSay, you want the deck to emit a card when the user drops a finger on it,\nand want the card to follow the finger until the user lifts it up.\nIn this situation, a widget that triggers a drag and a widget that is dragged are different.\nYou can implement it as follows:\n\n```python\nclass Card(KXDraggableBehavior, Widget):\n    pass\n\n\nclass Deck(Widget):\n    def on_touch_down(self, touch):\n        if self.collide_point(*touch.opos):\n            Card(...).start_dragging_from_others_touch(self, touch)\n```\n\n## Customization\n\nWhat draggables do `on_drag_succeed` / `on_drag_fail` / `on_drag_cancel` are completely customizable.\nFor example, by default, when a drag fails, the draggable will go back to where it came from with little animation.\nThis is because the default handler of `on_drag_fail` is implemented as follows:\n\n```python\nclass KXDraggableBehavior:\n    async def on_drag_fail(self, touch, ctx):\n        await ak.animate(\n            self, duration=.1,\n            x=ctx.original_pos_win[0],\n            y=ctx.original_pos_win[1],\n        )\n        restore_widget_state(self, ctx.original_state)\n```\n\nIf you don't need the animation, and want the draggable to go back instantly, overwrite the handler as follows:\n\n```python\nclass MyDraggable(KXDraggableBehavior, Widget):\n    def on_drag_fail(self, touch, ctx):\n        restore_widget_state(self, ctx.original_state)\n```\n\nOr if you want the draggable to not go back, and want it to stay the current position, overwrite the handler as follows:\n\n```python\nclass MyDraggable(KXDraggableBehavior, Widget):\n    def on_drag_fail(self, touch, ctx):\n        pass\n```\n\nAnother example: when a drag succeed, the draggable will become a child of droppable, by default.\nIf you don't like it, and want the draggable to fade-out,\noverwrite the handler as follows:\n\n```python\nclass MyDraggable(KXDraggableBehavior, Widget):\n    async def on_drag_succeed(self, touch, ctx):\n        import asynckivy\n        await asynckivy.animate(self, opacity=0)\n        self.parent.remove_widget(self)\n```\n\nJust like that, you have free rein to change those behaviors.\nBut note that **only the default handler of `on_drag_succeed` and `on_drag_fail`\ncan be an async function. Those two only.**\n\nYou might say \"What's the point of implementing a default handler as an async function,\nwhen you can just launch any number of tasks from a regular function by using ``asynckivy.start()``?\".\nWell, if you use ``asynckivy.start()``, that task will run independently from the dragging process,\nwhich means the draggable might fire ``on_drag_end`` and might start another drag while the task is still running.\nIf a default handler is an async function,\nits code will be a part of dragging process and is guaranteed to be finished before ``on_drag_end`` gets fired.\n\n## License\n\nThis software is released under the terms of the MIT License.\n\n[drag_n_drop]:https://github.com/kivy-garden/drag_n_drop\n[flutter]:https://api.flutter.dev/flutter/widgets/Draggable-class.html\n[youtube]:https://www.youtube.com/playlist?list=PLNdhqAjzeEGiepWKfP43Dh7IWqn3cQtpQ\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Drag & Drop Extension for Kivy",
    "version": "0.2.0",
    "project_urls": {
        "Homepage": "https://github.com/kivy-garden/draggable",
        "Repository": "https://github.com/kivy-garden/draggable"
    },
    "split_keywords": [
        "kivy"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "507de5309612b42728c31e3933a40907114c18fbe325a5c29f5df40727754181",
                "md5": "941a11639d4007ddcb25fb3367ccd41e",
                "sha256": "d3d7fc715dcb7da7b8d3c4a4f64d7e4ac0b6cb1c62b467875e5fafe1170610f7"
            },
            "downloads": -1,
            "filename": "kivy_garden_draggable-0.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "941a11639d4007ddcb25fb3367ccd41e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8.1,<4.0.0",
            "size": 9250,
            "upload_time": "2023-08-28T11:11:44",
            "upload_time_iso_8601": "2023-08-28T11:11:44.141393Z",
            "url": "https://files.pythonhosted.org/packages/50/7d/e5309612b42728c31e3933a40907114c18fbe325a5c29f5df40727754181/kivy_garden_draggable-0.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "53efe9077c4c0ad3f941dfc26706afbc494d444eacfcd4fc012f58adb7d5a5c4",
                "md5": "4e44fe99c608f5b4bc5594624f1b738a",
                "sha256": "e43be691c204dc8b09da34441a919c3a7de7b623cdfa0e727840dbff8efcb976"
            },
            "downloads": -1,
            "filename": "kivy_garden_draggable-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "4e44fe99c608f5b4bc5594624f1b738a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8.1,<4.0.0",
            "size": 10228,
            "upload_time": "2023-08-28T11:11:45",
            "upload_time_iso_8601": "2023-08-28T11:11:45.533879Z",
            "url": "https://files.pythonhosted.org/packages/53/ef/e9077c4c0ad3f941dfc26706afbc494d444eacfcd4fc012f58adb7d5a5c4/kivy_garden_draggable-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-28 11:11:45",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "kivy-garden",
    "github_project": "draggable",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "kivy-garden-draggable"
}
        
Elapsed time: 0.19848s