Sideband_CE/sbapp/kivymd/uix/chip/chip.py

1245 lines
34 KiB
Python
Raw Normal View History

2022-07-07 22:16:10 +02:00
"""
Components/Chip
===============
.. seealso::
2023-07-10 02:49:58 +02:00
`Material Design spec, Chips <https://m3.material.io/components/chips/overview>`_
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. rubric:: Chips can show multiple interactive elements together in the same
area, such as a list of selectable movie times, or a series of email
contacts. There are four types of chips: assist, filter, input, and
suggestion.
2022-07-07 22:16:10 +02:00
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/chips.png
:align: center
Usage
-----
2023-07-10 02:49:58 +02:00
.. tabs::
.. tab:: Declarative KV style
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDChip:
pos_hint: {"center_x": .5, "center_y": .5}
MDChipText:
text: "MDChip"
'''
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return Builder.load_string(KV)
Example().run()
.. tab:: Declarative Python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip, MDChipText
from kivymd.uix.screen import MDScreen
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return (
MDScreen(
MDChip(
MDChipText(
text="MDChip"
),
pos_hint={"center_x": .5, "center_y": .5},
)
)
)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/chip.png
:align: center
Anatomy
-------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/anatomy-chip.png
:align: center
1. Container
2. Label text
3. Leading icon or image (optional)
4. Trailing remove icon (optional, input & filter chips only)
Container
---------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/radius-chip.png
:align: center
All chips are slightly rounded with an 8dp corner.
Shadows and elevation
---------------------
Chip containers can be elevated if the placement requires protection, such as
on top of an image.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadows-elevation-chip.png
:align: center
The following types of chips are available:
-------------------------------------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/available-type-chips.png
:align: center
- Assist_
- Filter_
- Input_
- Suggestion_
.. Assist:
Assist
------
`Assist chips <https://m3.material.io/components/chips/guidelines#5dd1928c-1476-4029-bdc5-fde66fc0dcb1>`_
represent smart or automated actions that can span multiple apps, such as
opening a calendar event from the home screen. Assist chips function as
though the user asked an assistant to complete the action. They should appear
dynamically and contextually in a UI.
An alternative to assist chips are buttons, which should appear persistently
and consistently.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/assist-chip.png
:align: center
Example of assist
-----------------
2022-07-07 22:16:10 +02:00
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
2023-07-10 02:49:58 +02:00
<CommonLabel@MDLabel>
adaptive_size: True
theme_text_color: "Custom"
text_color: "#e6e9df"
<CommonAssistChip@MDChip>
# Custom attribute.
text: ""
icon: ""
# Chip attribute.
type: "assist"
md_bg_color: "#2a3127"
line_color: "grey"
elevation: 1
shadow_softness: 2
MDChipLeadingIcon:
icon: root.icon
theme_text_color: "Custom"
text_color: "#68896c"
MDChipText:
text: root.text
theme_text_color: "Custom"
text_color: "#e6e9df"
2022-07-07 22:16:10 +02:00
MDScreen:
2023-07-10 02:49:58 +02:00
FitImage:
source: "bg.png"
MDBoxLayout:
orientation: "vertical"
adaptive_size: True
pos_hint: {"center_y": .6, "center_x": .5}
CommonLabel:
text: "in 10 mins"
bold: True
pos_hint: {"center_x": .5}
CommonLabel:
text: "Therapy with Thea"
font_style: "H3"
padding_y: "12dp"
CommonLabel:
text: "Video call"
font_style: "H5"
pos_hint: {"center_x": .5}
MDBoxLayout:
adaptive_size: True
pos_hint: {"center_x": .5}
spacing: "12dp"
padding: 0, "24dp", 0, 0
CommonAssistChip:
text: "Home office"
icon: "map-marker"
CommonAssistChip:
text: "Chat"
icon: "message"
MDWidget:
2022-07-07 22:16:10 +02:00
'''
2023-07-10 02:49:58 +02:00
class Example(MDApp):
2022-07-07 22:16:10 +02:00
def build(self):
2023-07-10 02:49:58 +02:00
self.theme_cls.primary_palette = "Teal"
self.theme_cls.theme_style = "Dark"
2022-07-07 22:16:10 +02:00
return Builder.load_string(KV)
2023-07-10 02:49:58 +02:00
Example().run()
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/example-assist-chip.png
2022-07-07 22:16:10 +02:00
:align: center
2023-07-10 02:49:58 +02:00
.. Filter:
Filter
------
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
`Filter chips <https://m3.material.io/components/chips/guidelines#8d453d50-8d8e-43aa-9ae3-87ed134d2e64>`_
use tags or descriptive words to filter content. They can be a good alternative
to toggle buttons or checkboxes.
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Tapping on a filter chip activates it and appends a leading checkmark icon to
the starting edge of the chip label.
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/filter-chip.png
2022-07-07 22:16:10 +02:00
:align: center
2023-07-10 02:49:58 +02:00
Example of filtering
--------------------
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. code-block:: python
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
from kivy.lang import Builder
from kivy.properties import StringProperty, ListProperty
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip, MDChipText
from kivymd.uix.list import OneLineIconListItem
from kivymd.icon_definitions import md_icons
from kivymd.uix.screen import MDScreen
from kivymd.utils import asynckivy
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Builder.load_string(
'''
<CustomOneLineIconListItem>
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
IconLeftWidget:
icon: root.icon
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
<PreviewIconsScreen>
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDBoxLayout:
orientation: "vertical"
spacing: "14dp"
padding: "20dp"
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDTextField:
id: search_field
hint_text: "Search icon"
mode: "rectangle"
icon_left: "magnify"
on_text: root.set_list_md_icons(self.text, True)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDBoxLayout:
id: chip_box
spacing: "12dp"
adaptive_height: True
RecycleView:
id: rv
viewclass: "CustomOneLineIconListItem"
key_size: "height"
RecycleBoxLayout:
padding: dp(10)
default_size: None, dp(48)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
'''
)
class CustomOneLineIconListItem(OneLineIconListItem):
icon = StringProperty()
class PreviewIconsScreen(MDScreen):
filter = ListProperty() # list of tags for filtering icons
def set_filter_chips(self):
'''Asynchronously creates and adds chips to the container.'''
async def set_filter_chips():
for tag in ["Outline", "Off", "On"]:
await asynckivy.sleep(0)
chip = MDChip(
MDChipText(
text=tag,
),
type="filter",
md_bg_color="#303A29",
)
chip.bind(active=lambda x, y, z=tag: self.set_filter(y, z))
self.ids.chip_box.add_widget(chip)
asynckivy.start(set_filter_chips())
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def set_filter(self, active: bool, tag: str) -> None:
'''Sets a list of tags for filtering icons.'''
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
if active:
self.filter.append(tag)
else:
self.filter.remove(tag)
def set_list_md_icons(self, text="", search=False) -> None:
'''Builds a list of icons.'''
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def add_icon_item(name_icon):
self.ids.rv.data.append(
{
"icon": name_icon,
"text": name_icon,
}
)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
self.ids.rv.data = []
for name_icon in md_icons.keys():
for tag in self.filter:
if tag.lower() in name_icon:
if search:
if text in name_icon:
add_icon_item(name_icon)
else:
add_icon_item(name_icon)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
class Example(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = PreviewIconsScreen()
def build(self) -> PreviewIconsScreen:
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "LightGreen"
return self.screen
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def on_start(self) -> None:
self.screen.set_list_md_icons()
self.screen.set_filter_chips()
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/example-filtering-icons-chip.gif
2022-07-07 22:16:10 +02:00
:align: center
2023-07-10 02:49:58 +02:00
Tap a chip to select it. Multiple chips can be selected or unselected:
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. code-block:: python
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
from kivy.lang import Builder
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip, MDChipText
from kivymd.uix.screen import MDScreen
from kivymd.utils import asynckivy
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Builder.load_string(
'''
<ChipScreen>
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDBoxLayout:
orientation: "vertical"
spacing: "14dp"
padding: "20dp"
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDLabel:
adaptive_height: True
text: "Select Type"
MDStackLayout:
id: chip_box
spacing: "12dp"
adaptive_height: True
MDWidget:
MDFlatButton:
text: "Uncheck chips"
pos: "20dp", "20dp"
on_release: root.unchecks_chips()
'''
)
class ChipScreen(MDScreen):
async def create_chips(self):
'''Asynchronously creates and adds chips to the container.'''
for tag in ["Extra Soft", "Soft", "Medium", "Hard"]:
await asynckivy.sleep(0)
self.ids.chip_box.add_widget(
MDChip(
MDChipText(
text=tag,
),
type="filter",
md_bg_color="#303A29",
active=True,
)
)
def unchecks_chips(self) -> None:
'''Removes marks from all chips.'''
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
for chip in self.ids.chip_box.children:
if chip.active:
chip.active = False
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
class Example(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = ChipScreen()
def build(self) -> ChipScreen:
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "LightGreen"
return self.screen
def on_start(self) -> None:
asynckivy.start(self.screen.create_chips())
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/example-filtering-icons-chip-2.gif
:align: center
Alternatively, a single chip can be selected.
This offers an alternative to toggle buttons, radio buttons, or single select
menus:
2022-07-07 22:16:10 +02:00
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
2023-07-10 02:49:58 +02:00
from kivymd.uix.chip import MDChip, MDChipText
from kivymd.uix.screen import MDScreen
from kivymd.utils import asynckivy
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Builder.load_string(
'''
<ChipScreen>
2022-07-07 22:16:10 +02:00
MDBoxLayout:
orientation: "vertical"
2023-07-10 02:49:58 +02:00
spacing: "14dp"
padding: "20dp"
2022-07-07 22:16:10 +02:00
MDLabel:
2023-07-10 02:49:58 +02:00
adaptive_height: True
text: "Select Type"
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDStackLayout:
2022-07-07 22:16:10 +02:00
id: chip_box
2023-07-10 02:49:58 +02:00
spacing: "12dp"
adaptive_height: True
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDFillRoundFlatButton:
text: "Add to cart"
md_bg_color: "green"
size_hint_x: 1
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDWidget:
'''
)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
class ChipScreen(MDScreen):
async def create_chips(self):
'''Asynchronously creates and adds chips to the container.'''
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
for tag in ["Extra Soft", "Soft", "Medium", "Hard"]:
await asynckivy.sleep(0)
chip = MDChip(
MDChipText(
text=tag,
),
type="filter",
md_bg_color="#303A29",
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
)
chip.bind(active=self.uncheck_chip)
self.ids.chip_box.add_widget(chip)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def uncheck_chip(self, current_chip: MDChip, active: bool) -> None:
'''Removes a mark from an already marked chip.'''
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
if active:
for chip in self.ids.chip_box.children:
if current_chip is not chip:
if chip.active:
chip.active = False
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
class Example(MDApp):
2022-07-07 22:16:10 +02:00
def __init__(self, **kwargs):
super().__init__(**kwargs)
2023-07-10 02:49:58 +02:00
self.screen = ChipScreen()
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def build(self) -> ChipScreen:
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "LightGreen"
return self.screen
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def on_start(self) -> None:
asynckivy.start(self.screen.create_chips())
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Example().run()
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/example-filtering-single-select.gif
:align: center
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. Input:
Input
-----
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
`Input chips <https://m3.material.io/components/chips/guidelines#4d2d5ef5-3fcd-46e9-99f2-067747b2393f>`_
represent discrete pieces of information entered by a user, such as Gmail
contacts or filter options within a search field.
They enable user input and verify that input by converting text into chips.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/input-chip.png
:align: center
Example of input
----------------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDChip:
pos_hint: {"center_x": .5, "center_y": .5}
type: "input"
line_color: "grey"
_no_ripple_effect: True
MDChipLeadingAvatar:
source: "data/logo/kivy-icon-128.png"
MDChipText:
text: "MDChip"
MDChipTrailingIcon:
icon: "close"
'''
class Example(MDApp):
2022-07-07 22:16:10 +02:00
def build(self):
2023-07-10 02:49:58 +02:00
self.theme_cls.theme_style = "Dark"
2022-07-07 22:16:10 +02:00
return Builder.load_string(KV)
2023-07-10 02:49:58 +02:00
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/example-input-chip.png
:align: center
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. Suggestion:
Suggestion
----------
`Suggestion chips <https://m3.material.io/components/chips/guidelines#36d7bb16-a9bf-4cf6-a73d-8e05510d66a7>`_
help narrow a users intent by presenting dynamically generated suggestions,
such as possible responses or search filters.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/suggestion-chip.png
2022-07-07 22:16:10 +02:00
:align: center
2023-07-10 02:49:58 +02:00
Example of suggestion
---------------------
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. code-block::
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDChip:
pos_hint: {"center_x": .5, "center_y": .5}
type: "suggestion"
line_color: "grey"
MDChipText:
text: "MDChip"
'''
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return Builder.load_string(KV)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/example-suggestion.png
:align: center
API break
=========
1.1.1 version
-------------
2022-07-07 22:16:10 +02:00
.. code-block:: python
2023-07-10 02:49:58 +02:00
from kivy.lang import Builder
from kivymd.app import MDApp
2022-07-07 22:16:10 +02:00
KV = '''
2023-07-10 02:49:58 +02:00
MDScreen:
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
MDChip:
text: "Portland"
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.on_release_chip(self)
'''
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_release_chip(self, instance_check):
print(instance_check)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Test().run()
1.2.0 version
-------------
.. code-block:: python
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
from kivy.lang import Builder
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
from kivymd.app import MDApp
KV = '''
MDScreen:
MDChip:
pos_hint: {"center_x": .5, "center_y": .5}
line_color: "grey"
on_release: app.on_release_chip(self)
MDChipText:
text: "MDChip"
2022-07-07 22:16:10 +02:00
'''
2023-07-10 02:49:58 +02:00
class Example(MDApp):
def build(self):
return Builder.load_string(KV)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def on_release_chip(self, instance_check):
print(instance_check)
Example().run()
2022-07-07 22:16:10 +02:00
"""
2023-07-10 02:49:58 +02:00
from __future__ import annotations
__all__ = (
"MDChip",
"MDChipLeadingAvatar",
"MDChipLeadingIcon",
"MDChipTrailingIcon",
"MDChipText",
)
2022-07-07 22:16:10 +02:00
import os
2023-07-10 02:49:58 +02:00
from kivy import Logger
2022-07-07 22:16:10 +02:00
from kivy.animation import Animation
2023-07-10 02:49:58 +02:00
from kivy.clock import Clock
2022-07-07 22:16:10 +02:00
from kivy.lang import Builder
from kivy.metrics import dp
2023-07-10 02:49:58 +02:00
from kivy.properties import (
BooleanProperty,
ColorProperty,
OptionProperty,
StringProperty,
VariableListProperty,
)
2022-07-07 22:16:10 +02:00
from kivy.uix.behaviors import ButtonBehavior
from kivymd import uix_path
2023-07-10 02:49:58 +02:00
from kivymd.material_resources import DEVICE_TYPE
2022-07-07 22:16:10 +02:00
from kivymd.uix.behaviors import (
2023-07-10 02:49:58 +02:00
CircularRippleBehavior,
2022-10-08 17:17:59 +02:00
CommonElevationBehavior,
2022-07-07 22:16:10 +02:00
RectangularRippleBehavior,
2022-10-08 17:17:59 +02:00
ScaleBehavior,
2022-07-07 22:16:10 +02:00
TouchBehavior,
)
from kivymd.uix.boxlayout import MDBoxLayout
2023-07-10 02:49:58 +02:00
from kivymd.uix.label import MDIcon, MDLabel
2022-07-07 22:16:10 +02:00
with open(
os.path.join(uix_path, "chip", "chip.kv"), encoding="utf-8"
) as kv_file:
Builder.load_string(kv_file.read())
2023-07-10 02:49:58 +02:00
class BaseChipIcon(
CircularRippleBehavior, ScaleBehavior, ButtonBehavior, MDIcon
):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.ripple_scale = 1.5
Clock.schedule_once(self.adjust_icon_size)
def adjust_icon_size(self, *args) -> None:
# If the user has not changed the icon size, then we set the standard
# icon size according to the standards of material design version 3.
if (
self.font_name == "Icons"
and self.theme_cls.font_styles["Icon"][1] == self.font_size
):
self.font_size = (
"18sp"
if not self.source and not isinstance(self, MDChipLeadingAvatar)
else "24sp"
)
if self.source and isinstance(self, MDChipLeadingAvatar):
self.icon = self.source
self._size = [dp(28), dp(28)]
self.font_size = "28sp"
self.padding_x = "6dp"
self._no_ripple_effect = True
class LabelTextContainer(MDBoxLayout):
"""Implements a container for the chip label."""
class LeadingIconContainer(MDBoxLayout):
"""Implements a container for the leading icon."""
class TrailingIconContainer(MDBoxLayout):
"""Implements a container for the trailing icon."""
class MDChipLeadingAvatar(BaseChipIcon):
"""
Implements the leading avatar for the chip.
For more information, see in the
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
:class:`~kivymd.uix.behaviors.ScaleBehavior` and
:class:`~kivy.uix.behaviors.ButtonBehavior` and
:class:`~kivymd.uix.label.MDIcon`
classes documentation.
"""
class MDChipLeadingIcon(BaseChipIcon):
"""
Implements the leading icon for the chip.
For more information, see in the
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
:class:`~kivymd.uix.behaviors.ScaleBehavior` and
:class:`~kivy.uix.behaviors.ButtonBehavior` and
:class:`~kivymd.uix.label.MDIcon`
classes documentation.
"""
class MDChipTrailingIcon(BaseChipIcon):
"""
Implements the trailing icon for the chip.
For more information, see in the
:class:`~kivymd.uix.behaviors.CircularRippleBehavior` and
:class:`~kivymd.uix.behaviors.ScaleBehavior` and
:class:`~kivy.uix.behaviors.ButtonBehavior` and
:class:`~kivymd.uix.label.MDIcon`
classes documentation.
"""
class MDChipText(MDLabel):
"""
Implements the label for the chip.
For more information, see in the
:class:`~kivymd.uix.label.MDLabel` classes documentation.
"""
2022-07-07 22:16:10 +02:00
class MDChip(
2022-10-08 17:17:59 +02:00
MDBoxLayout,
2022-07-07 22:16:10 +02:00
RectangularRippleBehavior,
ButtonBehavior,
2022-10-08 17:17:59 +02:00
CommonElevationBehavior,
TouchBehavior,
2022-07-07 22:16:10 +02:00
):
2023-07-10 02:49:58 +02:00
"""
Chip class.
For more information, see in the
:class:`~kivymd.uix.boxlayout.MDBoxLayout` and
:class:`~kivymd.uix.behaviors.RectangularRippleBehavior` and
:class:`~kivy.uix.behaviors.ButtonBehavior` and
:class:`~kivymd.uix.behaviors.CommonElevationBehavior` and
:class:`~kivymd.uix.behaviors.TouchBehavior`
classes documentation.
"""
radius = VariableListProperty([dp(8)], length=4)
"""
Chip radius.
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
and defaults to `[dp(8), dp(8), dp(8), dp(8)]`.
"""
text = StringProperty(deprecated=True)
2022-07-07 22:16:10 +02:00
"""
Chip text.
2023-07-10 02:49:58 +02:00
.. deprecated:: 1.2.0
2022-07-07 22:16:10 +02:00
:attr:`text` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
2023-07-10 02:49:58 +02:00
type = OptionProperty(
"suggestion", options=["assist", "filter", "input", "suggestion"]
)
"""
Type of chip.
.. versionadded:: 1.2.0
Available options are: `'assist'`, `'filter'`, `'input'`, `'suggestion'`.
:attr:`type` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'suggestion'`.
"""
icon_left = StringProperty(deprecated=True)
2022-07-07 22:16:10 +02:00
"""
Chip left icon.
.. versionadded:: 1.0.0
2023-07-10 02:49:58 +02:00
.. deprecated:: 1.2.0
2022-07-07 22:16:10 +02:00
:attr:`icon_left` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
2023-07-10 02:49:58 +02:00
icon_right = StringProperty(deprecated=True)
2022-07-07 22:16:10 +02:00
"""
Chip right icon.
.. versionadded:: 1.0.0
2023-07-10 02:49:58 +02:00
.. deprecated:: 1.2.0
2022-07-07 22:16:10 +02:00
:attr:`icon_right` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
2023-07-10 02:49:58 +02:00
text_color = ColorProperty(None, deprecated=True)
2022-07-07 22:16:10 +02:00
"""
2023-07-10 02:49:58 +02:00
Chip's text color in (r, g, b, a) or string format.
.. deprecated:: 1.2.0
2022-07-07 22:16:10 +02:00
:attr:`text_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
2023-07-10 02:49:58 +02:00
icon_right_color = ColorProperty(None, deprecated=True)
2022-07-07 22:16:10 +02:00
"""
2023-07-10 02:49:58 +02:00
Chip's right icon color in (r, g, b, a) or string format.
2022-07-07 22:16:10 +02:00
.. versionadded:: 1.0.0
2023-07-10 02:49:58 +02:00
.. deprecated:: 1.2.0
2022-07-07 22:16:10 +02:00
:attr:`icon_right_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
2023-07-10 02:49:58 +02:00
icon_left_color = ColorProperty(None, deprecated=True)
2022-07-07 22:16:10 +02:00
"""
2023-07-10 02:49:58 +02:00
Chip's left icon color in (r, g, b, a) or string format.
2022-07-07 22:16:10 +02:00
.. versionadded:: 1.0.0
2023-07-10 02:49:58 +02:00
.. deprecated:: 1.2.0
2022-07-07 22:16:10 +02:00
:attr:`icon_left_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
icon_check_color = ColorProperty(None)
"""
2023-07-10 02:49:58 +02:00
Chip's check icon color in (r, g, b, a) or string format.
2022-07-07 22:16:10 +02:00
.. versionadded:: 1.0.0
:attr:`icon_check_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
active = BooleanProperty(False)
"""
Whether the check is marked or not.
.. versionadded:: 1.0.0
:attr:`active` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
2023-07-10 02:49:58 +02:00
selected_color = ColorProperty(None)
"""
The background color of the chip in the marked state in (r, g, b, a)
or string format.
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
.. versionadded:: 1.2.0
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
:attr:`selected_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
_current_md_bg_color = ColorProperty(None)
# A flag that disallow ripple animation of the chip
# at the time of clicking the chip icons.
_allow_chip_ripple = BooleanProperty(True)
# The flag signals the end of the ripple animation.
_anim_complete = BooleanProperty(False)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def on_long_touch(self, *args) -> None:
if self.type == "filter":
self.active = not self.active
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def on_type(self, instance, value: str) -> None:
"""Called when the values of :attr:`type` change."""
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def adjust_padding(*args):
"""
According to the type of chip, it sets the margins according
to the specification of the material design version 3.
"""
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
self.padding = {
"input": (
"12dp"
if not self.ids.leading_icon_container.children
else (
"5dp"
if not self.ids.leading_icon_container.children[
0
].source
else "16dp"
),
0,
"4dp",
0,
),
"assist": (
"16dp"
if not self.ids.leading_icon_container.children
else "8dp",
0,
"16dp"
if not self.ids.leading_icon_container.children
else "8dp",
0,
),
"suggestion": (
"16dp"
if not self.ids.leading_icon_container.children
else "8dp",
0,
"16dp",
0,
),
"filter": (
"16dp"
if not self.ids.leading_icon_container.children
else (
"8dp"
if not self.ids.leading_icon_container.children[
0
].source
else "4dp"
),
0,
"16dp"
if not self.ids.trailing_icon_container.children
else "8dp",
0,
),
}[value]
Clock.schedule_once(adjust_padding)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def on_active(self, instance_check, active_value: bool) -> None:
"""Called when the values of :attr:`active` change."""
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
if active_value:
self._current_md_bg_color = self.md_bg_color
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
Clock.schedule_once(self.complete_anim_ripple, 0.5)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def complete_anim_ripple(self, *args) -> None:
"""Called at the end of the ripple animation."""
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
if self.active:
if not self.ids.leading_icon_container.children:
if self.type == "filter":
self.add_marked_icon_to_chip()
self.set_chip_bg_color(
self.selected_color
if self.selected_color
else self.theme_cls.primary_color
)
else:
if (
self.ids.leading_icon_container.children
and self.ids.leading_icon_container.children[0].icon == "check"
):
if self.type == "filter":
self.remove_marked_icon_from_chip()
self.set_chip_bg_color(self._current_md_bg_color)
def remove_marked_icon_from_chip(self) -> None:
def remove_marked_icon_from_chip(*args):
self.ids.leading_icon_container.clear_widgets()
if self.ids.leading_icon_container.children:
anim = Animation(scale_value_x=0, scale_value_y=0, d=0.2)
anim.bind(on_complete=remove_marked_icon_from_chip)
anim.start(self.ids.leading_icon_container.children[0])
Animation(
padding=[dp(16), 0, dp(16), 0],
spacing=0,
d=0.2,
).start(self)
def add_marked_icon_to_chip(self) -> None:
"""Adds and animates a check icon to the chip."""
icon_check = MDChipLeadingIcon(
icon="check",
pos_hint={"center_y": 0.5},
font_size=dp(18),
scale_value_x=0,
scale_value_y=0,
)
icon_check.bind(
on_press=self._set_allow_chip_ripple,
on_release=self._set_allow_chip_ripple,
)
self.ids.leading_icon_container.add_widget(icon_check)
# Animating the scale of the icon.
Animation(scale_value_x=1, scale_value_y=1, d=0.2).start(icon_check)
# Animating the padding of the chip.
Animation(
padding=[dp(18), 0, 0, 0],
spacing=dp(18) if self.type == "filter" else 0,
d=0.2,
).start(self)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def set_chip_bg_color(self, color: list | str) -> None:
"""Animates the background color of the chip."""
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
if color:
Animation(md_bg_color=color, d=0.2).start(self)
self._anim_complete = not self._anim_complete
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def on_press(self, *args):
if self.active:
self.active = False
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def add_widget(self, widget, *args, **kwargs):
def add_icon_leading_trailing(container):
if len(container.children):
type_icon = (
"'leading'"
if isinstance(
widget, (MDChipLeadingIcon, MDChipLeadingAvatar)
2022-07-07 22:16:10 +02:00
)
2023-07-10 02:49:58 +02:00
else "'trailing'"
2022-07-07 22:16:10 +02:00
)
2023-07-10 02:49:58 +02:00
Logger.warning(
f"KivyMD: "
f"Do not use more than one {type_icon} icon. "
f"This is contrary to the material design rules "
f"of version 3"
)
return
if isinstance(widget, MDChipTrailingIcon) and self.type in [
"assist",
"suggestion",
]:
Logger.warning(
f"KivyMD: "
f"According to the material design standards of version "
f"3, do not use the trailing icon for an '{self.type}' "
f"type chip."
)
return
if (
isinstance(widget, MDChipTrailingIcon)
and self.type == "filter"
and DEVICE_TYPE == "mobile"
):
Logger.warning(
"KivyMD: "
"According to the material design standards of version 3, "
"only on desktop computers and tablets, filter chips can "
"contain a finishing icon for directly removing the chip "
"or opening the options menu."
)
return
if (
isinstance(widget, (MDChipLeadingIcon, MDChipLeadingAvatar))
and self.type == "filter"
):
Logger.warning(
"KivyMD: "
"According to the material design standards of version 3, "
"it is better not to use a leading icon for a 'filter' "
"type chip."
)
if (
isinstance(widget, MDChipLeadingAvatar)
and self.type == "suggestion"
):
Logger.warning(
"KivyMD: "
"According to the material design standards of version 3, "
"it is better not to use a leading avatar for a "
"'suggestion' type chip."
)
return
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
widget.bind(
on_press=self._set_allow_chip_ripple,
on_release=self._set_allow_chip_ripple,
)
widget.pos_hint = {"center_y": 0.5}
self.padding = ("8dp", 0, "8dp", 0)
self.spacing = (
"8dp"
if isinstance(
widget,
(
MDChipLeadingIcon,
MDChipLeadingAvatar,
MDChipTrailingIcon,
),
)
else 0
)
container.add_widget(widget)
if isinstance(widget, MDChipText):
widget.adaptive_size = True
widget.pos_hint = {"center_y": 0.5}
if self.type == "suggestion":
self.padding = ("16dp", 0, "16dp", 0)
Clock.schedule_once(
lambda x: self.ids.label_container.add_widget(widget)
)
elif isinstance(widget, (MDChipLeadingIcon, MDChipLeadingAvatar)):
Clock.schedule_once(
lambda x: add_icon_leading_trailing(
self.ids.leading_icon_container
)
)
elif isinstance(widget, MDChipTrailingIcon):
Clock.schedule_once(
lambda x: add_icon_leading_trailing(
self.ids.trailing_icon_container
)
)
elif isinstance(
widget,
(LabelTextContainer, LeadingIconContainer, TrailingIconContainer),
2022-07-07 22:16:10 +02:00
):
2023-07-10 02:49:58 +02:00
return super().add_widget(widget)
2022-07-07 22:16:10 +02:00
2023-07-10 02:49:58 +02:00
def _set_allow_chip_ripple(
self, instance: MDChipLeadingIcon | MDChipTrailingIcon
) -> None:
self._allow_chip_ripple = not self._allow_chip_ripple