2022-07-07 22:16:10 +02:00
|
|
|
|
"""
|
|
|
|
|
Components/Selection
|
|
|
|
|
====================
|
|
|
|
|
|
|
|
|
|
.. seealso::
|
|
|
|
|
|
|
|
|
|
`Material Design spec, Banner <https://material.io/design/interaction/selection.html>`_
|
|
|
|
|
|
|
|
|
|
.. rubric:: Selection refers to how users indicate specific items they intend to take action on.
|
|
|
|
|
|
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/selection-previous.png
|
|
|
|
|
:align: center
|
|
|
|
|
|
|
|
|
|
Entering selection mode
|
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
|
|
To select an item and enter selection mode, long press the item:
|
|
|
|
|
|
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/enter-selection-mode.gif
|
|
|
|
|
:align: center
|
|
|
|
|
|
|
|
|
|
Exiting selection mode
|
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
|
|
To exit selection mode, tap each selected item until they’re all deselected:
|
|
|
|
|
|
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/exit-selection-mode.gif
|
|
|
|
|
:align: center
|
|
|
|
|
|
|
|
|
|
Larger selections
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
.. note:: This feature is missing yet.
|
|
|
|
|
|
|
|
|
|
Events
|
|
|
|
|
------
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
def on_selected(self, instance_selection_list, instance_selection_item):
|
|
|
|
|
'''Called when a list item is selected.'''
|
|
|
|
|
|
|
|
|
|
def on_unselected(self, instance_selection_list, instance_selection_item):
|
|
|
|
|
'''Called when a list item is unselected.'''
|
|
|
|
|
|
|
|
|
|
Example with TwoLineAvatarListItem
|
|
|
|
|
----------------------------------
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
from kivy.animation import Animation
|
|
|
|
|
from kivy.lang import Builder
|
2022-10-02 17:16:59 +02:00
|
|
|
|
from kivy.utils import get_color_from_hex
|
2022-07-07 22:16:10 +02:00
|
|
|
|
|
|
|
|
|
from kivymd.app import MDApp
|
|
|
|
|
from kivymd.uix.list import TwoLineAvatarListItem
|
|
|
|
|
|
|
|
|
|
KV = '''
|
|
|
|
|
<MyItem>
|
|
|
|
|
text: "Two-line item with avatar"
|
|
|
|
|
secondary_text: "Secondary text here"
|
|
|
|
|
_no_ripple_effect: True
|
|
|
|
|
|
|
|
|
|
ImageLeftWidget:
|
|
|
|
|
source: "data/logo/kivy-icon-256.png"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MDBoxLayout:
|
|
|
|
|
orientation: "vertical"
|
|
|
|
|
|
|
|
|
|
MDTopAppBar:
|
|
|
|
|
id: toolbar
|
|
|
|
|
title: "Inbox"
|
|
|
|
|
left_action_items: [["menu"]]
|
|
|
|
|
right_action_items: [["magnify"], ["dots-vertical"]]
|
|
|
|
|
md_bg_color: 0, 0, 0, 1
|
|
|
|
|
|
|
|
|
|
MDBoxLayout:
|
|
|
|
|
padding: "24dp", "8dp", 0, "8dp"
|
|
|
|
|
adaptive_size: True
|
|
|
|
|
|
|
|
|
|
MDLabel:
|
|
|
|
|
text: "Today"
|
|
|
|
|
adaptive_size: True
|
|
|
|
|
|
|
|
|
|
ScrollView:
|
|
|
|
|
|
|
|
|
|
MDSelectionList:
|
|
|
|
|
id: selection_list
|
|
|
|
|
spacing: "12dp"
|
|
|
|
|
overlay_color: app.overlay_color[:-1] + [.2]
|
|
|
|
|
icon_bg_color: app.overlay_color
|
|
|
|
|
on_selected: app.on_selected(*args)
|
|
|
|
|
on_unselected: app.on_unselected(*args)
|
|
|
|
|
on_selected_mode: app.set_selection_mode(*args)
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MyItem(TwoLineAvatarListItem):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Example(MDApp):
|
2022-10-02 17:16:59 +02:00
|
|
|
|
overlay_color = get_color_from_hex("#6042e4")
|
2022-07-07 22:16:10 +02:00
|
|
|
|
|
|
|
|
|
def build(self):
|
|
|
|
|
return Builder.load_string(KV)
|
|
|
|
|
|
|
|
|
|
def on_start(self):
|
|
|
|
|
for i in range(10):
|
|
|
|
|
self.root.ids.selection_list.add_widget(MyItem())
|
|
|
|
|
|
|
|
|
|
def set_selection_mode(self, instance_selection_list, mode):
|
|
|
|
|
if mode:
|
|
|
|
|
md_bg_color = self.overlay_color
|
|
|
|
|
left_action_items = [
|
|
|
|
|
[
|
|
|
|
|
"close",
|
|
|
|
|
lambda x: self.root.ids.selection_list.unselected_all(),
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
right_action_items = [["trash-can"], ["dots-vertical"]]
|
|
|
|
|
else:
|
|
|
|
|
md_bg_color = (0, 0, 0, 1)
|
|
|
|
|
left_action_items = [["menu"]]
|
|
|
|
|
right_action_items = [["magnify"], ["dots-vertical"]]
|
|
|
|
|
self.root.ids.toolbar.title = "Inbox"
|
|
|
|
|
|
|
|
|
|
Animation(md_bg_color=md_bg_color, d=0.2).start(self.root.ids.toolbar)
|
|
|
|
|
self.root.ids.toolbar.left_action_items = left_action_items
|
|
|
|
|
self.root.ids.toolbar.right_action_items = right_action_items
|
|
|
|
|
|
|
|
|
|
def on_selected(self, instance_selection_list, instance_selection_item):
|
|
|
|
|
self.root.ids.toolbar.title = str(
|
|
|
|
|
len(instance_selection_list.get_selected_list_items())
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def on_unselected(self, instance_selection_list, instance_selection_item):
|
|
|
|
|
if instance_selection_list.get_selected_list_items():
|
|
|
|
|
self.root.ids.toolbar.title = str(
|
|
|
|
|
len(instance_selection_list.get_selected_list_items())
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Example().run()
|
|
|
|
|
|
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/selection-example-with-listItem.gif
|
|
|
|
|
:align: center
|
|
|
|
|
|
|
|
|
|
Example with FitImage
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
from kivy.animation import Animation
|
|
|
|
|
from kivy.lang import Builder
|
|
|
|
|
from kivy.properties import ColorProperty
|
|
|
|
|
|
|
|
|
|
from kivymd.app import MDApp
|
2022-10-02 17:16:59 +02:00
|
|
|
|
from kivymd.uix.fitimage import FitImage
|
2022-07-07 22:16:10 +02:00
|
|
|
|
|
|
|
|
|
KV = '''
|
|
|
|
|
MDBoxLayout:
|
|
|
|
|
orientation: "vertical"
|
|
|
|
|
md_bg_color: app.theme_cls.bg_light
|
|
|
|
|
|
|
|
|
|
MDTopAppBar:
|
|
|
|
|
id: toolbar
|
|
|
|
|
title: "Inbox"
|
|
|
|
|
left_action_items: [["menu"]]
|
|
|
|
|
right_action_items: [["magnify"], ["dots-vertical"]]
|
|
|
|
|
md_bg_color: app.theme_cls.bg_light
|
|
|
|
|
specific_text_color: 0, 0, 0, 1
|
|
|
|
|
|
|
|
|
|
MDBoxLayout:
|
|
|
|
|
padding: "24dp", "8dp", 0, "8dp"
|
|
|
|
|
adaptive_size: True
|
|
|
|
|
|
|
|
|
|
MDLabel:
|
|
|
|
|
text: "Today"
|
|
|
|
|
adaptive_size: True
|
|
|
|
|
|
|
|
|
|
ScrollView:
|
|
|
|
|
|
|
|
|
|
MDSelectionList:
|
|
|
|
|
id: selection_list
|
|
|
|
|
padding: "24dp", 0, "24dp", "24dp"
|
|
|
|
|
cols: 3
|
|
|
|
|
spacing: "12dp"
|
|
|
|
|
overlay_color: app.overlay_color[:-1] + [.2]
|
|
|
|
|
icon_bg_color: app.overlay_color
|
|
|
|
|
progress_round_color: app.progress_round_color
|
|
|
|
|
on_selected: app.on_selected(*args)
|
|
|
|
|
on_unselected: app.on_unselected(*args)
|
|
|
|
|
on_selected_mode: app.set_selection_mode(*args)
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Example(MDApp):
|
|
|
|
|
overlay_color = ColorProperty("#6042e4")
|
|
|
|
|
progress_round_color = "#ef514b"
|
|
|
|
|
|
|
|
|
|
def build(self):
|
|
|
|
|
return Builder.load_string(KV)
|
|
|
|
|
|
|
|
|
|
def on_start(self):
|
|
|
|
|
for i in range(10):
|
|
|
|
|
self.root.ids.selection_list.add_widget(
|
|
|
|
|
FitImage(
|
|
|
|
|
source="image.png",
|
|
|
|
|
size_hint_y=None,
|
|
|
|
|
height="240dp",
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def set_selection_mode(self, instance_selection_list, mode):
|
|
|
|
|
if mode:
|
|
|
|
|
md_bg_color = self.overlay_color
|
|
|
|
|
left_action_items = [
|
|
|
|
|
[
|
|
|
|
|
"close",
|
|
|
|
|
lambda x: self.root.ids.selection_list.unselected_all(),
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
right_action_items = [["trash-can"], ["dots-vertical"]]
|
|
|
|
|
else:
|
|
|
|
|
md_bg_color = (1, 1, 1, 1)
|
|
|
|
|
left_action_items = [["menu"]]
|
|
|
|
|
right_action_items = [["magnify"], ["dots-vertical"]]
|
|
|
|
|
self.root.ids.toolbar.title = "Inbox"
|
|
|
|
|
|
|
|
|
|
Animation(md_bg_color=md_bg_color, d=0.2).start(self.root.ids.toolbar)
|
|
|
|
|
self.root.ids.toolbar.left_action_items = left_action_items
|
|
|
|
|
self.root.ids.toolbar.right_action_items = right_action_items
|
|
|
|
|
|
|
|
|
|
def on_selected(self, instance_selection_list, instance_selection_item):
|
|
|
|
|
self.root.ids.toolbar.title = str(
|
|
|
|
|
len(instance_selection_list.get_selected_list_items())
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def on_unselected(self, instance_selection_list, instance_selection_item):
|
|
|
|
|
if instance_selection_list.get_selected_list_items():
|
|
|
|
|
self.root.ids.toolbar.title = str(
|
|
|
|
|
len(instance_selection_list.get_selected_list_items())
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Example().run()
|
|
|
|
|
|
|
|
|
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/selection-example-with-fitimage.gif
|
|
|
|
|
:align: center
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__all__ = ("MDSelectionList",)
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
from typing import Union
|
|
|
|
|
|
|
|
|
|
from kivy.animation import Animation
|
|
|
|
|
from kivy.clock import Clock
|
|
|
|
|
from kivy.graphics.context_instructions import Color
|
|
|
|
|
from kivy.graphics.vertex_instructions import (
|
|
|
|
|
Ellipse,
|
|
|
|
|
RoundedRectangle,
|
|
|
|
|
SmoothLine,
|
|
|
|
|
)
|
|
|
|
|
from kivy.lang import Builder
|
|
|
|
|
from kivy.metrics import dp
|
|
|
|
|
from kivy.properties import (
|
|
|
|
|
BooleanProperty,
|
|
|
|
|
ColorProperty,
|
|
|
|
|
ListProperty,
|
|
|
|
|
NumericProperty,
|
|
|
|
|
ObjectProperty,
|
|
|
|
|
StringProperty,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
from kivymd import uix_path
|
|
|
|
|
from kivymd.theming import ThemableBehavior
|
|
|
|
|
from kivymd.uix.behaviors import TouchBehavior
|
|
|
|
|
from kivymd.uix.button import MDIconButton
|
|
|
|
|
from kivymd.uix.list import MDList
|
|
|
|
|
from kivymd.uix.relativelayout import MDRelativeLayout
|
|
|
|
|
|
|
|
|
|
with open(
|
|
|
|
|
os.path.join(uix_path, "selection", "selection.kv"), encoding="utf-8"
|
|
|
|
|
) as kv_file:
|
|
|
|
|
Builder.load_string(kv_file.read())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SelectionIconCheck(MDIconButton):
|
|
|
|
|
"""Implements the icon for the checked item."""
|
|
|
|
|
|
|
|
|
|
scale = NumericProperty(0)
|
|
|
|
|
icon_check_color = ColorProperty([0, 0, 0, 1])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SelectionItem(ThemableBehavior, MDRelativeLayout, TouchBehavior):
|
|
|
|
|
selected = BooleanProperty(False)
|
|
|
|
|
"""
|
|
|
|
|
Whether or not an item is checked.
|
|
|
|
|
|
|
|
|
|
:attr:`selected` is an :class:`~kivy.properties.BooleanProperty`
|
|
|
|
|
and defaults to `False`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
owner = ObjectProperty()
|
|
|
|
|
"""
|
|
|
|
|
Instance of :class:`~kivymd.uix.selection.MDSelectionList` class.
|
|
|
|
|
|
|
|
|
|
:attr:`owner` is an :class:`~kivy.properties.ObjectProperty`
|
|
|
|
|
and defaults to `None`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
instance_item = ObjectProperty()
|
|
|
|
|
"""
|
|
|
|
|
User item. Must be a Kivy or KivyMD widget.
|
|
|
|
|
|
|
|
|
|
:attr:`instance_item` is an :class:`~kivy.properties.ObjectProperty`
|
|
|
|
|
and defaults to `None`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
instance_icon = ObjectProperty()
|
|
|
|
|
"""
|
|
|
|
|
Instance of :class:`~kivymd.uix.selection.SelectionIconCheck` class.
|
|
|
|
|
|
|
|
|
|
:attr:`instance_icon` is an :class:`~kivy.properties.ObjectProperty`
|
|
|
|
|
and defaults to `None`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
overlay_color = ColorProperty([0, 0, 0, 0.2])
|
|
|
|
|
"""See :attr:`~MDSelectionList.overlay_color`."""
|
|
|
|
|
|
|
|
|
|
progress_round_size = NumericProperty(dp(46))
|
|
|
|
|
"""See :attr:`~MDSelectionList.progress_round_size`."""
|
|
|
|
|
|
|
|
|
|
progress_round_color = ColorProperty(None)
|
|
|
|
|
"""See :attr:`~MDSelectionList.progress_round_color`."""
|
|
|
|
|
|
|
|
|
|
_progress_round = NumericProperty(0)
|
|
|
|
|
_progress_line_end = NumericProperty(0)
|
|
|
|
|
_progress_animation = BooleanProperty(False)
|
|
|
|
|
_touch_long = BooleanProperty(False)
|
|
|
|
|
_instance_progress_inner_circle_color = ObjectProperty()
|
|
|
|
|
_instance_progress_inner_circle_ellipse = ObjectProperty()
|
|
|
|
|
_instance_progress_inner_outer_color = ObjectProperty()
|
|
|
|
|
_instance_progress_inner_outer_line = ObjectProperty()
|
|
|
|
|
_instance_overlay_color = ObjectProperty()
|
|
|
|
|
_instance_overlay_rounded_rec = ObjectProperty()
|
|
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
|
Clock.schedule_once(self.set_progress_round)
|
|
|
|
|
|
|
|
|
|
def set_progress_round(self, interval: Union[int, float]) -> None:
|
|
|
|
|
with self.canvas.after:
|
|
|
|
|
self._instance_progress_inner_circle_color = Color(
|
|
|
|
|
rgba=(0, 0, 0, 0)
|
|
|
|
|
)
|
|
|
|
|
self._instance_progress_inner_circle_ellipse = Ellipse(
|
|
|
|
|
size=self.get_progress_round_size(),
|
|
|
|
|
pos=self.get_progress_round_pos(),
|
|
|
|
|
)
|
|
|
|
|
self.bind(
|
|
|
|
|
pos=self.update_progress_inner_circle_ellipse,
|
|
|
|
|
size=self.update_progress_inner_circle_ellipse,
|
|
|
|
|
)
|
|
|
|
|
# FIXME: Radius value is not displayed.
|
|
|
|
|
self._instance_overlay_color = Color(rgba=(0, 0, 0, 0))
|
|
|
|
|
self._instance_overlay_rounded_rec = RoundedRectangle(
|
|
|
|
|
size=self.size,
|
|
|
|
|
pos=self.pos,
|
|
|
|
|
radius=self.instance_item.radius
|
|
|
|
|
if hasattr(self.instance_item, "radius")
|
|
|
|
|
else [
|
|
|
|
|
0,
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
self.bind(
|
|
|
|
|
pos=self.update_overlay_rounded_rec,
|
|
|
|
|
size=self.update_overlay_rounded_rec,
|
|
|
|
|
)
|
|
|
|
|
self._instance_progress_inner_outer_color = Color(rgba=(0, 0, 0, 0))
|
|
|
|
|
self._instance_progress_inner_outer_line = SmoothLine(
|
|
|
|
|
width=dp(4),
|
|
|
|
|
circle=[
|
|
|
|
|
self.center_x,
|
|
|
|
|
self.center_y,
|
|
|
|
|
self.progress_round_size * 0.58,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def do_selected_item(self, *args) -> None:
|
|
|
|
|
Animation(scale=1, d=0.2).start(self.instance_icon)
|
|
|
|
|
self.selected = True
|
|
|
|
|
self._progress_animation = False
|
|
|
|
|
self._instance_overlay_color.rgba = self.get_overlay_color()
|
|
|
|
|
self.owner.dispatch("on_selected", self)
|
|
|
|
|
|
|
|
|
|
def do_unselected_item(self) -> None:
|
|
|
|
|
Animation(scale=0, d=0.2).start(self.instance_icon)
|
|
|
|
|
self.selected = False
|
|
|
|
|
self._instance_overlay_color.rgba = self.get_overlay_color()
|
|
|
|
|
self.owner.dispatch("on_unselected", self)
|
|
|
|
|
|
|
|
|
|
def do_animation_progress_line(
|
|
|
|
|
self, animation: Animation, instance_selection_item, value: float
|
|
|
|
|
) -> None:
|
|
|
|
|
self._instance_progress_inner_outer_line.circle = (
|
|
|
|
|
self.center_x,
|
|
|
|
|
self.center_y,
|
|
|
|
|
self.progress_round_size * 0.58,
|
|
|
|
|
0,
|
|
|
|
|
360 * value,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def update_overlay_rounded_rec(self, *args) -> None:
|
|
|
|
|
self._instance_overlay_rounded_rec.size = self.size
|
|
|
|
|
self._instance_overlay_rounded_rec.pos = self.pos
|
|
|
|
|
|
|
|
|
|
def update_progress_inner_circle_ellipse(self, *args) -> None:
|
|
|
|
|
self._instance_progress_inner_circle_ellipse.size = (
|
|
|
|
|
self.get_progress_round_size()
|
|
|
|
|
)
|
|
|
|
|
self._instance_progress_inner_circle_ellipse.pos = (
|
|
|
|
|
self.get_progress_round_pos()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def reset_progress_animation(self) -> None:
|
|
|
|
|
Animation.cancel_all(self)
|
|
|
|
|
self._progress_animation = False
|
|
|
|
|
self._instance_progress_inner_circle_color.rgba = (0, 0, 0, 0)
|
|
|
|
|
self._instance_progress_inner_outer_color.rgba = (0, 0, 0, 0)
|
|
|
|
|
self._instance_progress_inner_outer_line.circle = [
|
|
|
|
|
self.center_x,
|
|
|
|
|
self.center_y,
|
|
|
|
|
self.progress_round_size * 0.58,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
]
|
|
|
|
|
self._progress_line_end = 0
|
|
|
|
|
|
|
|
|
|
def get_overlay_color(self) -> list:
|
|
|
|
|
return self.overlay_color if self.selected else (0, 0, 0, 0)
|
|
|
|
|
|
|
|
|
|
def get_progress_round_pos(self) -> tuple:
|
|
|
|
|
return (
|
2022-10-02 17:16:59 +02:00
|
|
|
|
(self.pos[0] + self.width / 2) - self.progress_round_size / 2,
|
2022-07-07 22:16:10 +02:00
|
|
|
|
self.center_y - self.progress_round_size / 2,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def get_progress_round_size(self) -> tuple:
|
|
|
|
|
return self.progress_round_size, self.progress_round_size
|
|
|
|
|
|
|
|
|
|
def get_progress_round_color(self) -> tuple:
|
|
|
|
|
return (
|
|
|
|
|
self.theme_cls.primary_color
|
|
|
|
|
if not self.progress_round_color
|
|
|
|
|
else self.progress_round_color
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def get_progress_line_color(self) -> tuple:
|
|
|
|
|
return (
|
|
|
|
|
self.theme_cls.primary_color[:-1] + [0.5]
|
|
|
|
|
if not self.progress_round_color
|
|
|
|
|
else self.progress_round_color[:-1] + [0.5]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def on_long_touch(self, *args) -> None:
|
|
|
|
|
if not self.owner.get_selected():
|
|
|
|
|
self._touch_long = True
|
|
|
|
|
self._progress_animation = True
|
|
|
|
|
|
|
|
|
|
def on_touch_up(self, touch):
|
|
|
|
|
if self.collide_point(*touch.pos):
|
|
|
|
|
if self._touch_long:
|
|
|
|
|
self._touch_long = False
|
|
|
|
|
return super().on_touch_up(touch)
|
|
|
|
|
|
|
|
|
|
def on_touch_down(self, touch):
|
|
|
|
|
if self.collide_point(*touch.pos):
|
|
|
|
|
if self.selected:
|
|
|
|
|
self.do_unselected_item()
|
|
|
|
|
else:
|
|
|
|
|
if self.owner.selected_mode:
|
|
|
|
|
self.do_selected_item()
|
|
|
|
|
return super().on_touch_down(touch)
|
|
|
|
|
|
|
|
|
|
def on__touch_long(self, instance_selection_tem, touch_value: bool) -> None:
|
|
|
|
|
if not touch_value:
|
|
|
|
|
self.reset_progress_animation()
|
|
|
|
|
|
|
|
|
|
def on__progress_animation(
|
|
|
|
|
self, instance_selection_tem, touch_value: bool
|
|
|
|
|
) -> None:
|
|
|
|
|
if touch_value:
|
|
|
|
|
anim = Animation(_progress_line_end=360, d=1, t="in_out_quad")
|
|
|
|
|
anim.bind(
|
|
|
|
|
on_progress=self.do_animation_progress_line,
|
|
|
|
|
on_complete=self.do_selected_item,
|
|
|
|
|
)
|
|
|
|
|
anim.start(self)
|
|
|
|
|
self._instance_progress_inner_outer_color.rgba = (
|
|
|
|
|
self.get_progress_line_color()
|
|
|
|
|
)
|
|
|
|
|
self._instance_progress_inner_circle_color.rgba = (
|
|
|
|
|
self.get_progress_round_color()
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
self.reset_progress_animation()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MDSelectionList(MDList):
|
|
|
|
|
"""
|
|
|
|
|
:Events:
|
|
|
|
|
`on_selected`
|
|
|
|
|
Called when a list item is selected.
|
|
|
|
|
`on_unselected`
|
|
|
|
|
Called when a list item is unselected.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
selected_mode = BooleanProperty(False)
|
|
|
|
|
"""
|
|
|
|
|
List item selection mode. If `True` when clicking on a list item, it will
|
|
|
|
|
be selected.
|
|
|
|
|
|
|
|
|
|
:attr:`selected_mode` is an :class:`~kivy.properties.BooleanProperty`
|
|
|
|
|
and defaults to `False`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
icon = StringProperty("check")
|
|
|
|
|
"""
|
|
|
|
|
Name of the icon with which the selected list item will be marked.
|
|
|
|
|
|
|
|
|
|
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
|
|
|
|
|
and defaults to `'check'`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
icon_pos = ListProperty()
|
|
|
|
|
"""
|
|
|
|
|
The position of the icon that will mark the selected list item.
|
|
|
|
|
|
|
|
|
|
:attr:`icon_pos` is an :class:`~kivy.properties.ListProperty`
|
|
|
|
|
and defaults to `[]`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
icon_bg_color = ColorProperty([1, 1, 1, 1])
|
|
|
|
|
"""
|
|
|
|
|
Background color of the icon that will mark the selected list item.
|
|
|
|
|
|
|
|
|
|
:attr:`icon_bg_color` is an :class:`~kivy.properties.ColorProperty`
|
|
|
|
|
and defaults to `[1, 1, 1, 1]`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
icon_check_color = ColorProperty([0, 0, 0, 1])
|
|
|
|
|
"""
|
|
|
|
|
Color of the icon that will mark the selected list item.
|
|
|
|
|
|
|
|
|
|
:attr:`icon_check_color` is an :class:`~kivy.properties.ColorProperty`
|
|
|
|
|
and defaults to `[1, 1, 1, 1]`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
overlay_color = ColorProperty([0, 0, 0, 0.2])
|
|
|
|
|
"""
|
|
|
|
|
The overlay color of the selected list item..
|
|
|
|
|
|
|
|
|
|
:attr:`overlay_color` is an :class:`~kivy.properties.ColorProperty`
|
|
|
|
|
and defaults to `[0, 0, 0, 0.2]]`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
progress_round_size = NumericProperty(dp(46))
|
|
|
|
|
"""
|
|
|
|
|
Size of the spinner for switching of `selected_mode` mode.
|
|
|
|
|
|
|
|
|
|
:attr:`progress_round_size` is an :class:`~kivy.properties.NumericProperty`
|
|
|
|
|
and defaults to `dp(46)`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
progress_round_color = ColorProperty(None)
|
|
|
|
|
"""
|
|
|
|
|
Color of the spinner for switching of `selected_mode` mode.
|
|
|
|
|
|
|
|
|
|
:attr:`progress_round_color` is an :class:`~kivy.properties.NumericProperty`
|
|
|
|
|
and defaults to `None`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
|
self.register_event_type("on_selected")
|
|
|
|
|
self.register_event_type("on_unselected")
|
|
|
|
|
|
|
|
|
|
def add_widget(self, widget, index=0, canvas=None):
|
|
|
|
|
selection_icon = SelectionIconCheck(
|
|
|
|
|
icon=self.icon,
|
|
|
|
|
md_bg_color=self.icon_bg_color,
|
|
|
|
|
icon_check_color=self.icon_check_color,
|
|
|
|
|
)
|
|
|
|
|
container = SelectionItem(
|
|
|
|
|
size_hint=(1, None),
|
|
|
|
|
height=widget.height,
|
|
|
|
|
instance_item=widget,
|
|
|
|
|
instance_icon=selection_icon,
|
|
|
|
|
overlay_color=self.overlay_color,
|
|
|
|
|
progress_round_size=self.progress_round_size,
|
|
|
|
|
progress_round_color=self.progress_round_color,
|
|
|
|
|
owner=self,
|
|
|
|
|
)
|
|
|
|
|
container.add_widget(widget)
|
|
|
|
|
|
|
|
|
|
if not self.icon_pos:
|
|
|
|
|
pos = (
|
|
|
|
|
dp(12),
|
|
|
|
|
container.height / 2 - selection_icon.height / 2,
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
pos = self.icon_pos
|
|
|
|
|
selection_icon.pos = pos
|
|
|
|
|
container.add_widget(selection_icon)
|
|
|
|
|
return super().add_widget(container, index, canvas)
|
|
|
|
|
|
|
|
|
|
def get_selected(self) -> bool:
|
|
|
|
|
"""Returns ``True`` if at least one item in the list is checked."""
|
|
|
|
|
|
|
|
|
|
selected = False
|
|
|
|
|
for item in self.children:
|
|
|
|
|
if item.selected:
|
|
|
|
|
selected = True
|
|
|
|
|
break
|
|
|
|
|
return selected
|
|
|
|
|
|
|
|
|
|
def get_selected_list_items(self) -> list:
|
|
|
|
|
"""
|
|
|
|
|
Returns a list of marked objects:
|
|
|
|
|
|
|
|
|
|
[<kivymd.uix.selection.SelectionItem object>, ...]
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
selected_list_items = []
|
|
|
|
|
for item in self.children:
|
|
|
|
|
if item.selected:
|
|
|
|
|
selected_list_items.append(item)
|
|
|
|
|
return selected_list_items
|
|
|
|
|
|
|
|
|
|
def unselected_all(self) -> None:
|
|
|
|
|
for item in self.children:
|
|
|
|
|
item.do_unselected_item()
|
|
|
|
|
self.selected_mode = False
|
|
|
|
|
|
|
|
|
|
def selected_all(self) -> None:
|
|
|
|
|
for item in self.children:
|
|
|
|
|
item.do_selected_item()
|
|
|
|
|
self.selected_mode = True
|
|
|
|
|
|
|
|
|
|
def on_selected(self, *args):
|
|
|
|
|
"""Called when a list item is selected."""
|
|
|
|
|
|
|
|
|
|
if not self.selected_mode:
|
|
|
|
|
self.selected_mode = True
|
|
|
|
|
|
|
|
|
|
def on_unselected(self, *args):
|
|
|
|
|
"""Called when a list item is unselected."""
|
|
|
|
|
|
|
|
|
|
self.selected_mode = self.get_selected()
|