test-kivy-app/kivy_venv/lib/python3.11/site-packages/kivymd/uix/chip/chip.py

1256 lines
35 KiB
Python
Raw Normal View History

2024-09-15 12:12:16 +00:00
"""
Components/Chip
===============
.. seealso::
2024-09-15 17:57:02 +00:00
`Material Design 3 spec, Chips <https://m3.material.io/components/chips/overview>`_
2024-09-15 12:12:16 +00: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.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/chips.png
:align: center
Usage
-----
2024-09-15 17:57:02 +00:00
.. code-block:: kv
MDChip:
MDChipLeadingAvatar: # MDChipLeadingIcon
MDChipText:
MDChipTrailingIcon:
Anatomy
=======
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/chip-anatomy.png
:align: center
Example
-------
2024-09-15 12:12:16 +00: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
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:
2024-09-15 17:57:02 +00:00
2024-09-15 12:12:16 +00:00
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
-----------------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
<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"
MDScreen:
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:
'''
class Example(MDApp):
def build(self):
self.theme_cls.primary_palette = "Teal"
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-assist-chip.png
:align: center
.. Filter:
2024-09-15 17:57:02 +00:00
2024-09-15 12:12:16 +00:00
Filter
------
`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.
Tapping on a filter chip activates it and appends a leading checkmark icon to
the starting edge of the chip label.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/filter-chip.png
:align: center
Example of filtering
--------------------
.. code-block:: python
from kivy.lang import Builder
from kivy.properties import StringProperty, ListProperty
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip, MDChipText
2024-09-15 17:57:02 +00:00
from kivymd.uix.list import MDListItem
2024-09-15 12:12:16 +00:00
from kivymd.icon_definitions import md_icons
from kivymd.uix.screen import MDScreen
2024-09-15 17:57:02 +00:00
import asynckivy
2024-09-15 12:12:16 +00:00
Builder.load_string(
'''
<CustomOneLineIconListItem>
2024-09-15 17:57:02 +00:00
MDListItemLeadingIcon:
2024-09-15 12:12:16 +00:00
icon: root.icon
2024-09-15 17:57:02 +00:00
MDListItemHeadlineText:
text: root.text
2024-09-15 12:12:16 +00:00
<PreviewIconsScreen>
MDBoxLayout:
orientation: "vertical"
spacing: "14dp"
padding: "20dp"
MDTextField:
id: search_field
2024-09-15 17:57:02 +00:00
mode: "outlined"
2024-09-15 12:12:16 +00:00
on_text: root.set_list_md_icons(self.text, True)
2024-09-15 17:57:02 +00:00
MDTextFieldLeadingIcon:
icon: "magnify"
MDTextFieldHintText:
text: "Search icon"
2024-09-15 12:12:16 +00: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"
'''
)
2024-09-15 17:57:02 +00:00
class CustomOneLineIconListItem(MDListItem):
2024-09-15 12:12:16 +00:00
icon = StringProperty()
2024-09-15 17:57:02 +00:00
text = StringProperty()
2024-09-15 12:12:16 +00:00
class PreviewIconsScreen(MDScreen):
filter = ListProperty() # list of tags for filtering icons
2024-09-15 17:57:02 +00:00
2024-09-15 12:12:16 +00:00
def set_filter_chips(self):
'''Asynchronously creates and adds chips to the container.'''
2024-09-15 17:57:02 +00:00
2024-09-15 12:12:16 +00:00
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)
2024-09-15 17:57:02 +00:00
2024-09-15 12:12:16 +00:00
asynckivy.start(set_filter_chips())
def set_filter(self, active: bool, tag: str) -> None:
'''Sets a list of tags for filtering icons.'''
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.'''
def add_icon_item(name_icon):
self.ids.rv.data.append(
{
"icon": name_icon,
"text": name_icon,
}
)
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)
class Example(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = PreviewIconsScreen()
def build(self) -> PreviewIconsScreen:
self.theme_cls.theme_style = "Dark"
return self.screen
def on_start(self) -> None:
self.screen.set_list_md_icons()
self.screen.set_filter_chips()
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/example-filtering-icons-chip.gif
:align: center
Tap a chip to select it. Multiple chips can be selected or unselected:
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip, MDChipText
from kivymd.uix.screen import MDScreen
2024-09-15 17:57:02 +00:00
import asynckivy
2024-09-15 12:12:16 +00:00
Builder.load_string(
'''
<ChipScreen>
MDBoxLayout:
orientation: "vertical"
spacing: "14dp"
padding: "20dp"
MDLabel:
adaptive_height: True
text: "Select Type"
MDStackLayout:
id: chip_box
spacing: "12dp"
adaptive_height: True
MDWidget:
2024-09-15 17:57:02 +00:00
MDButton:
2024-09-15 12:12:16 +00:00
pos: "20dp", "20dp"
on_release: root.unchecks_chips()
2024-09-15 17:57:02 +00:00
MDButtonText:
text: "Uncheck chips"
2024-09-15 12:12:16 +00:00
'''
)
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.'''
for chip in self.ids.chip_box.children:
if chip.active:
chip.active = False
class Example(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = ChipScreen()
def build(self) -> ChipScreen:
self.theme_cls.theme_style = "Dark"
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:
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip, MDChipText
from kivymd.uix.screen import MDScreen
2024-09-15 17:57:02 +00:00
import asynckivy
2024-09-15 12:12:16 +00:00
Builder.load_string(
'''
<ChipScreen>
MDBoxLayout:
orientation: "vertical"
spacing: "14dp"
padding: "20dp"
MDLabel:
adaptive_height: True
text: "Select Type"
MDStackLayout:
id: chip_box
spacing: "12dp"
adaptive_height: True
MDWidget:
'''
)
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)
chip = MDChip(
MDChipText(
text=tag,
),
type="filter",
md_bg_color="#303A29",
)
chip.bind(active=self.uncheck_chip)
self.ids.chip_box.add_widget(chip)
def uncheck_chip(self, current_chip: MDChip, active: bool) -> None:
'''Removes a mark from an already marked chip.'''
if active:
for chip in self.ids.chip_box.children:
if current_chip is not chip:
if chip.active:
chip.active = False
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-single-select.gif
:align: center
.. Input:
2024-09-15 17:57:02 +00:00
2024-09-15 12:12:16 +00:00
Input
-----
`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):
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-input-chip.png
:align: center
.. Suggestion:
2024-09-15 17:57:02 +00:00
2024-09-15 12:12:16 +00:00
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
:align: center
Example of suggestion
---------------------
.. 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
=========
2024-09-15 17:57:02 +00:00
1.2.0 version
2024-09-15 12:12:16 +00:00
-------------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDChip:
text: "Portland"
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.on_release_chip(self)
'''
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_release_chip(self, instance_check):
print(instance_check)
Test().run()
2024-09-15 17:57:02 +00:00
2.0.0 version
2024-09-15 12:12:16 +00:00
-------------
.. code-block:: python
from kivy.lang import Builder
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"
'''
class Example(MDApp):
def build(self):
return Builder.load_string(KV)
def on_release_chip(self, instance_check):
print(instance_check)
Example().run()
"""
from __future__ import annotations
__all__ = (
"MDChip",
"MDChipLeadingAvatar",
"MDChipLeadingIcon",
"MDChipTrailingIcon",
"MDChipText",
)
import os
from kivy import Logger
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import (
BooleanProperty,
ColorProperty,
OptionProperty,
VariableListProperty,
)
from kivy.uix.behaviors import ButtonBehavior
from kivymd import uix_path
from kivymd.material_resources import DEVICE_TYPE
from kivymd.uix.behaviors import (
CircularRippleBehavior,
CommonElevationBehavior,
RectangularRippleBehavior,
ScaleBehavior,
TouchBehavior,
)
2024-09-15 17:57:02 +00:00
from kivymd.uix.behaviors.state_layer_behavior import StateLayerBehavior
2024-09-15 12:12:16 +00:00
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.label import MDIcon, MDLabel
with open(
os.path.join(uix_path, "chip", "chip.kv"), encoding="utf-8"
) as kv_file:
Builder.load_string(kv_file.read())
class BaseChipIcon(
CircularRippleBehavior, ScaleBehavior, ButtonBehavior, MDIcon
):
2024-09-15 17:57:02 +00:00
icon_color = ColorProperty(None)
"""
Button icon color in (r, g, b, a) or string format.
:attr:`icon_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
icon_color_disabled = ColorProperty(None)
"""
The icon color in (r, g, b, a) or string format of the chip when
the chip is disabled.
.. versionadded:: 2.0.0
:attr:`icon_color_disabled` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
_type = OptionProperty(
"suggestion", options=["assist", "filter", "input", "suggestion"]
)
2024-09-15 12:12:16 +00:00
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"
2024-09-15 17:57:02 +00:00
and self.theme_cls.font_styles["Icon"]["large"]["font-size"]
== self.font_size
2024-09-15 12:12:16 +00:00
):
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
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.behaviors.ripple_behavior.CircularRippleBehavior` and
:class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior` and
2024-09-15 12:12:16 +00:00
:class:`~kivy.uix.behaviors.ButtonBehavior` and
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.label.label.MDIcon`
2024-09-15 12:12:16 +00:00
classes documentation.
"""
class MDChipLeadingIcon(BaseChipIcon):
"""
Implements the leading icon for the chip.
For more information, see in the
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.behaviors.ripple_behavior.CircularRippleBehavior` and
:class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior` and
2024-09-15 12:12:16 +00:00
:class:`~kivy.uix.behaviors.ButtonBehavior` and
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.label.label.MDIcon`
2024-09-15 12:12:16 +00:00
classes documentation.
"""
class MDChipTrailingIcon(BaseChipIcon):
"""
Implements the trailing icon for the chip.
For more information, see in the
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.behaviors.ripple_behavior.CircularRippleBehavior` and
:class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior` and
2024-09-15 12:12:16 +00:00
:class:`~kivy.uix.behaviors.ButtonBehavior` and
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.label.label.MDIcon`
2024-09-15 12:12:16 +00:00
classes documentation.
"""
class MDChipText(MDLabel):
"""
Implements the label for the chip.
For more information, see in the
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.label.label.MDLabel` classes documentation.
2024-09-15 12:12:16 +00:00
"""
2024-09-15 17:57:02 +00:00
text_color_disabled = ColorProperty(None)
"""
The text color in (r, g, b, a) or string format of the chip when
the chip is disabled.
.. versionadded:: 2.0.0
:attr:`text_color_disabled` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
_type = OptionProperty(
"suggestion", options=["assist", "filter", "input", "suggestion"]
)
2024-09-15 12:12:16 +00:00
class MDChip(
MDBoxLayout,
RectangularRippleBehavior,
ButtonBehavior,
CommonElevationBehavior,
TouchBehavior,
2024-09-15 17:57:02 +00:00
StateLayerBehavior,
2024-09-15 12:12:16 +00:00
):
"""
Chip class.
For more information, see in the
:class:`~kivymd.uix.boxlayout.MDBoxLayout` and
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.behaviors.ripple_behavior.RectangularRippleBehavior` and
2024-09-15 12:12:16 +00:00
:class:`~kivy.uix.behaviors.ButtonBehavior` and
2024-09-15 17:57:02 +00:00
:class:`~kivymd.uix.behaviors.elevation.CommonElevationBehavior` and
:class:`~kivymd.uix.behaviors.touch_behavior.TouchBehavior`
2024-09-15 12:12:16 +00:00
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)]`.
"""
type = OptionProperty(
"suggestion", options=["assist", "filter", "input", "suggestion"]
)
"""
Type of chip.
2024-09-15 17:57:02 +00:00
.. versionadded:: 2.0.0
2024-09-15 12:12:16 +00:00
Available options are: `'assist'`, `'filter'`, `'input'`, `'suggestion'`.
:attr:`type` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'suggestion'`.
"""
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`.
"""
selected_color = ColorProperty(None)
"""
The background color of the chip in the marked state in (r, g, b, a)
or string format.
2024-09-15 17:57:02 +00:00
.. versionadded:: 2.0.0
2024-09-15 12:12:16 +00:00
:attr:`selected_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
2024-09-15 17:57:02 +00:00
line_color_disabled = ColorProperty(None)
"""
The color of the outline in the disabled state
.. versionadded:: 2.0.0
:attr:`line_color_disabled` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
_line_color = ColorProperty(None)
2024-09-15 12:12:16 +00: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)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def on_long_touch(self, *args) -> None:
2024-09-15 17:57:02 +00:00
"""Fired when the widget is pressed for a long time."""
2024-09-15 12:12:16 +00:00
if self.type == "filter":
self.active = not self.active
2024-09-15 17:57:02 +00:00
def on_line_color(self, instance, value) -> None:
"""Fired when the values of :attr:`line_color` change."""
if not self.disabled:
self._line_color = value
2024-09-15 12:12:16 +00:00
def on_type(self, instance, value: str) -> None:
2024-09-15 17:57:02 +00:00
"""Fired when the values of :attr:`type` change."""
2024-09-15 12:12:16 +00: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.
"""
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)
def on_active(self, instance_check, active_value: bool) -> None:
"""Called when the values of :attr:`active` change."""
if active_value:
self._current_md_bg_color = self.md_bg_color
Clock.schedule_once(self.complete_anim_ripple, 0.5)
def complete_anim_ripple(self, *args) -> None:
"""Called at the end of the ripple animation."""
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
2024-09-15 17:57:02 +00:00
else {
"filter": self.theme_cls.surfaceContainerLowColor,
"suggestion": self.theme_cls.surfaceContainerLowColor,
"input": self.theme_cls.surfaceContainerLowColor,
"assist": self.theme_cls.surfaceContainerLowColor,
}[self.type]
if self.theme_bg_color == "Primary"
else self.md_bg_color
2024-09-15 12:12:16 +00:00
)
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)
def set_chip_bg_color(self, color: list | str) -> None:
"""Animates the background color of the chip."""
if color:
Animation(md_bg_color=color, d=0.2).start(self)
self._anim_complete = not self._anim_complete
2024-09-15 17:57:02 +00:00
def on_press(self, *args) -> None:
"""Fired when the button is pressed."""
2024-09-15 12:12:16 +00:00
if self.active:
self.active = False
2024-09-15 17:57:02 +00:00
self._on_press(args)
def on_release(self, *args) -> None:
"""
Fired when the button is released
(i.e. the touch/click that pressed the button goes away).
"""
self._on_release(args)
2024-09-15 12:12:16 +00:00
def add_widget(self, widget, *args, **kwargs):
2024-09-15 17:57:02 +00:00
def set_type(*args):
widget._type = self.type
2024-09-15 12:12:16 +00:00
def add_icon_leading_trailing(container):
if len(container.children):
type_icon = (
"'leading'"
if isinstance(
widget, (MDChipLeadingIcon, MDChipLeadingAvatar)
)
else "'trailing'"
)
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
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):
2024-09-15 17:57:02 +00:00
Clock.schedule_once(set_type)
2024-09-15 12:12:16 +00:00
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)):
2024-09-15 17:57:02 +00:00
Clock.schedule_once(set_type)
2024-09-15 12:12:16 +00:00
Clock.schedule_once(
lambda x: add_icon_leading_trailing(
self.ids.leading_icon_container
)
)
elif isinstance(widget, MDChipTrailingIcon):
2024-09-15 17:57:02 +00:00
Clock.schedule_once(set_type)
2024-09-15 12:12:16 +00:00
Clock.schedule_once(
lambda x: add_icon_leading_trailing(
self.ids.trailing_icon_container
)
)
elif isinstance(
widget,
(LabelTextContainer, LeadingIconContainer, TrailingIconContainer),
):
2024-09-15 17:57:02 +00:00
if isinstance(
widget, (LeadingIconContainer, TrailingIconContainer)
):
Clock.schedule_once(set_type)
2024-09-15 12:12:16 +00:00
return super().add_widget(widget)
def _set_allow_chip_ripple(
self, instance: MDChipLeadingIcon | MDChipTrailingIcon
) -> None:
self._allow_chip_ripple = not self._allow_chip_ripple