working condition
This commit is contained in:
parent
417e54da96
commit
511e0b0379
517 changed files with 29187 additions and 32696 deletions
|
@ -5,10 +5,8 @@ from kivy.uix.floatlayout import FloatLayout
|
|||
from kivy.uix.label import Label
|
||||
from kivy.uix.screenmanager import Screen
|
||||
|
||||
from kivymd.uix.behaviors import SpecificBackgroundColorBehavior
|
||||
|
||||
|
||||
class MDAdaptiveWidget(SpecificBackgroundColorBehavior):
|
||||
class MDAdaptiveWidget:
|
||||
adaptive_height = BooleanProperty(False)
|
||||
"""
|
||||
If `True`, the following properties will be applied to the widget:
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -15,7 +15,7 @@ AnchorLayout
|
|||
AnchorLayout:
|
||||
canvas:
|
||||
Color:
|
||||
rgba: app.theme_cls.primary_color
|
||||
rgba: app.theme_cls.primaryColor
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
|
@ -26,7 +26,7 @@ MDAnchorLayout
|
|||
.. code-block:: kv
|
||||
|
||||
MDAnchorLayout:
|
||||
md_bg_color: app.theme_cls.primary_color
|
||||
md_bg_color: app.theme_cls.primaryColor
|
||||
"""
|
||||
|
||||
__all__ = ("MDAnchorLayout",)
|
||||
|
@ -35,13 +35,24 @@ from kivy.uix.anchorlayout import AnchorLayout
|
|||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior, BackgroundColorBehavior
|
||||
|
||||
|
||||
class MDAnchorLayout(
|
||||
DeclarativeBehavior, ThemableBehavior, AnchorLayout, MDAdaptiveWidget
|
||||
DeclarativeBehavior,
|
||||
ThemableBehavior,
|
||||
BackgroundColorBehavior,
|
||||
AnchorLayout,
|
||||
MDAdaptiveWidget,
|
||||
):
|
||||
"""
|
||||
Anchor layout class. For more information, see in the
|
||||
:class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation.
|
||||
Anchor layout class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
|
||||
:class:`~kivymd.theming.ThemableBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.backgroundcolor_behavior.BackgroundColorBehavior` and
|
||||
:class:`~kivy.uix.anchorlayout.AnchorLayout` and
|
||||
:class:`~kivymd.uix.MDAdaptiveWidget`
|
||||
classes documentation.
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# NOQA F401
|
||||
from .appbar import (
|
||||
MDTopAppBar,
|
||||
MDTopAppBarTitle,
|
||||
MDBottomAppBar,
|
||||
MDActionTopAppBarButton,
|
||||
MDActionBottomAppBarButton,
|
||||
MDFabBottomAppBarButton,
|
||||
MDTopAppBarLeadingButtonContainer,
|
||||
MDTopAppBarTrailingButtonContainer,
|
||||
)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,110 @@
|
|||
<MDTopAppBarLeadingButtonContainer>
|
||||
size_hint_x: None
|
||||
width: self.minimum_width
|
||||
padding: "8dp", 0, "16dp", 0
|
||||
|
||||
|
||||
<MDTopAppBarTrailingButtonContainer>
|
||||
size_hint_x: None
|
||||
width: self.minimum_width
|
||||
padding: "16dp", 0, "16dp", 0
|
||||
spacing: "4dp"
|
||||
|
||||
|
||||
<MDActionTopAppBarButton>
|
||||
pos_hint: {"center_y": .5}
|
||||
color:
|
||||
self.theme_cls.onSurfaceVariantColor \
|
||||
if self.theme_icon_color == "Primary" else \
|
||||
self.icon_color
|
||||
|
||||
|
||||
<MDTopAppBarTitle>
|
||||
padding:
|
||||
[
|
||||
self._appbar._left_padding if self._appbar else 0,
|
||||
0,
|
||||
self._appbar._right_padding if self._appbar else 0,
|
||||
0,
|
||||
]
|
||||
font_style:
|
||||
{ \
|
||||
"small": "Title", \
|
||||
"medium": "Headline", \
|
||||
"large": "Headline", \
|
||||
}[self._appbar.type if self._appbar else "small"]
|
||||
role:
|
||||
{ \
|
||||
"small": "large", \
|
||||
"medium": "small", \
|
||||
"large": "medium", \
|
||||
}[self._appbar.type if self._appbar else "large"]
|
||||
adaptive_width:
|
||||
( \
|
||||
True \
|
||||
if self._appbar.type == "small" else \
|
||||
False \
|
||||
) \
|
||||
if self._appbar else True
|
||||
size_hint_x:
|
||||
( \
|
||||
None \
|
||||
if self._appbar.type == "small" else \
|
||||
1 \
|
||||
) \
|
||||
if self._appbar else None
|
||||
|
||||
|
||||
<MDTopAppBar>
|
||||
canvas:
|
||||
Color:
|
||||
group: "md-top-app-bar-color"
|
||||
rgba:
|
||||
self.theme_cls.surfaceColor \
|
||||
if self.theme_bg_color == "Primary" else \
|
||||
self.md_bg_color
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
|
||||
orientation:
|
||||
"vertical" \
|
||||
if self.type in ("medium", "large") else \
|
||||
"horizontal"
|
||||
size_hint_y: None
|
||||
height:
|
||||
{ \
|
||||
"small": "64dp", \
|
||||
"medium": "112dp", \
|
||||
"large": "152dp", \
|
||||
}[self.type]
|
||||
|
||||
BoxLayout:
|
||||
id: root_box
|
||||
|
||||
BoxLayout:
|
||||
id: title_box
|
||||
padding: "16dp", 0, "16dp", 0
|
||||
|
||||
|
||||
<MDFabBottomAppBarButton>
|
||||
elevation_level: 0
|
||||
theme_shadow_color: "Custom"
|
||||
shadow_color: self.theme_cls.transparentColor
|
||||
|
||||
|
||||
<MDBottomAppBar>
|
||||
size_hint_y: None
|
||||
height: "80dp"
|
||||
elevation_level:
|
||||
2 \
|
||||
if self.theme_elevation_level == "Primary" else \
|
||||
self.elevation_level
|
||||
shadow_softness:
|
||||
2 \
|
||||
if self.theme_shadow_softness == "Primary" else \
|
||||
self.shadow_softness
|
||||
md_bg_color:
|
||||
self.theme_cls.surfaceContainerColor \
|
||||
if self.theme_bg_color == "Primary" else \
|
||||
self.md_bg_color
|
1272
kivy_venv/lib/python3.11/site-packages/kivymd/uix/appbar/appbar.py
Normal file
1272
kivy_venv/lib/python3.11/site-packages/kivymd/uix/appbar/appbar.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
|||
from .backdrop import MDBackdrop # NOQA F401
|
Binary file not shown.
Binary file not shown.
|
@ -1,50 +0,0 @@
|
|||
<MDBackdrop>
|
||||
md_bg_color:
|
||||
root.theme_cls.primary_color \
|
||||
if not root.back_layer_color \
|
||||
else root.back_layer_color
|
||||
|
||||
MDBackdropToolbar:
|
||||
id: toolbar
|
||||
type_height: "small"
|
||||
anchor_title: root.anchor_title
|
||||
title: root.title
|
||||
elevation: 0
|
||||
left_action_items: root.left_action_items
|
||||
right_action_items: root.right_action_items
|
||||
pos_hint: {"top": 1}
|
||||
md_bg_color:
|
||||
root.theme_cls.primary_color \
|
||||
if not root.back_layer_color \
|
||||
else root.back_layer_color
|
||||
|
||||
_BackLayer:
|
||||
id: back_layer
|
||||
y: -toolbar.height
|
||||
padding: 0, 0, 0, toolbar.height + dp(10)
|
||||
|
||||
_FrontLayer:
|
||||
id: _front_layer
|
||||
md_bg_color: 0, 0, 0, 0
|
||||
orientation: "vertical"
|
||||
size_hint_y: None
|
||||
height: root.height - toolbar.height
|
||||
padding: root.padding
|
||||
md_bg_color:
|
||||
root.theme_cls.bg_normal \
|
||||
if not root.front_layer_color \
|
||||
else root.front_layer_color
|
||||
radius:
|
||||
[root.radius_left, root.radius_right,
|
||||
0, 0]
|
||||
|
||||
OneLineListItem:
|
||||
id: header_button
|
||||
text: root.header_text
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
on_press: root.open()
|
||||
|
||||
MDBoxLayout:
|
||||
id: front_layer
|
||||
padding: 0, 0, 0, "10dp"
|
|
@ -1,548 +0,0 @@
|
|||
"""
|
||||
Components/Backdrop
|
||||
===================
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Material Design spec, Backdrop <https://material.io/components/backdrop>`_
|
||||
|
||||
.. rubric:: Skeleton layout for using :class:`~MDBackdrop`:
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop.png
|
||||
:align: center
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
<Root>
|
||||
|
||||
MDBackdrop:
|
||||
|
||||
MDBackdropBackLayer:
|
||||
|
||||
ContentForBackdropBackLayer:
|
||||
|
||||
MDBackdropFrontLayer:
|
||||
|
||||
ContentForBackdropFrontLayer:
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: Declarative KV styles
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from kivy.lang import Builder
|
||||
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.app import MDApp
|
||||
|
||||
# Your layouts.
|
||||
Builder.load_string(
|
||||
'''
|
||||
#:import os os
|
||||
#:import Window kivy.core.window.Window
|
||||
#:import IconLeftWidget kivymd.uix.list.IconLeftWidget
|
||||
#:import images_path kivymd.images_path
|
||||
|
||||
|
||||
<ItemBackdropFrontLayer@TwoLineAvatarListItem>
|
||||
icon: "android"
|
||||
|
||||
IconLeftWidget:
|
||||
icon: root.icon
|
||||
|
||||
|
||||
<MyBackdropFrontLayer@ItemBackdropFrontLayer>
|
||||
backdrop: None
|
||||
text: "Lower the front layer"
|
||||
secondary_text: " by 50 %"
|
||||
icon: "transfer-down"
|
||||
on_press: root.backdrop.open(-Window.height / 2)
|
||||
pos_hint: {"top": 1}
|
||||
_no_ripple_effect: True
|
||||
|
||||
|
||||
<MyBackdropBackLayer@Image>
|
||||
size_hint: .8, .8
|
||||
source: os.path.join(images_path, "logo", "kivymd-icon-512.png")
|
||||
pos_hint: {"center_x": .5, "center_y": .6}
|
||||
'''
|
||||
)
|
||||
|
||||
# Usage example of MDBackdrop.
|
||||
Builder.load_string(
|
||||
'''
|
||||
<ExampleBackdrop>
|
||||
|
||||
MDBackdrop:
|
||||
id: backdrop
|
||||
left_action_items: [['menu', lambda x: self.open()]]
|
||||
title: "Example Backdrop"
|
||||
radius_left: "25dp"
|
||||
radius_right: "0dp"
|
||||
header_text: "Menu:"
|
||||
|
||||
MDBackdropBackLayer:
|
||||
MyBackdropBackLayer:
|
||||
id: backlayer
|
||||
|
||||
MDBackdropFrontLayer:
|
||||
MyBackdropFrontLayer:
|
||||
backdrop: backdrop
|
||||
'''
|
||||
)
|
||||
|
||||
|
||||
class ExampleBackdrop(MDScreen):
|
||||
pass
|
||||
|
||||
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "Orange"
|
||||
return ExampleBackdrop()
|
||||
|
||||
|
||||
Example().run()
|
||||
|
||||
.. tab:: Declarative python styles
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import os
|
||||
|
||||
from kivy.core.window import Window
|
||||
from kivy.uix.image import Image
|
||||
|
||||
from kivymd import images_path
|
||||
from kivymd.uix.backdrop import MDBackdrop
|
||||
from kivymd.uix.backdrop.backdrop import (
|
||||
MDBackdropBackLayer, MDBackdropFrontLayer
|
||||
)
|
||||
from kivymd.uix.list import TwoLineAvatarListItem, IconLeftWidget
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.app import MDApp
|
||||
|
||||
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "Orange"
|
||||
|
||||
return (
|
||||
MDScreen(
|
||||
MDBackdrop(
|
||||
MDBackdropBackLayer(
|
||||
Image(
|
||||
size_hint=(0.8, 0.8),
|
||||
source=os.path.join(images_path, "logo", "kivymd-icon-512.png"),
|
||||
pos_hint={"center_x": 0.5, "center_y": 0.6},
|
||||
)
|
||||
),
|
||||
MDBackdropFrontLayer(
|
||||
TwoLineAvatarListItem(
|
||||
IconLeftWidget(icon="transfer-down"),
|
||||
text="Lower the front layer",
|
||||
secondary_text=" by 50 %",
|
||||
on_press=self.backdrop_open_by_50_percent,
|
||||
pos_hint={"top": 1},
|
||||
_no_ripple_effect=True,
|
||||
),
|
||||
),
|
||||
id="backdrop",
|
||||
title="Example Backdrop",
|
||||
radius_left="25dp",
|
||||
radius_right="0dp",
|
||||
header_text="Menu:",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def backdrop_open_by_50_percent(self, *args):
|
||||
self.root.ids.backdrop.open(-Window.height / 2)
|
||||
|
||||
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop.gif
|
||||
:align: center
|
||||
|
||||
.. Note:: `See full example <https://github.com/kivymd/KivyMD/wiki/Components-Backdrop>`_
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
"MDBackdropToolbar",
|
||||
"MDBackdropFrontLayer",
|
||||
"MDBackdropBackLayer",
|
||||
"MDBackdrop",
|
||||
)
|
||||
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.clock import Clock
|
||||
from kivy.lang import Builder
|
||||
from kivy.properties import (
|
||||
BooleanProperty,
|
||||
ColorProperty,
|
||||
ListProperty,
|
||||
NumericProperty,
|
||||
OptionProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.card import MDCard
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
from kivymd.uix.toolbar.toolbar import ActionTopAppBarButton, MDTopAppBar
|
||||
|
||||
with open(
|
||||
os.path.join(uix_path, "backdrop", "backdrop.kv"),
|
||||
encoding="utf-8",
|
||||
) as kv_file:
|
||||
Builder.load_string(kv_file.read())
|
||||
|
||||
|
||||
class MDBackdrop(MDFloatLayout):
|
||||
"""
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.floatlayout.MDFloatLayout` class documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_open`
|
||||
When the front layer drops.
|
||||
:attr:`on_close`
|
||||
When the front layer rises.
|
||||
"""
|
||||
|
||||
anchor_title = OptionProperty("left", options=["left", "center", "right"])
|
||||
"""
|
||||
Position toolbar title. Only used with `material_style = 'M3'`
|
||||
Available options are: `'left'`, `'center'`, `'right'`.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-anchor-title.png
|
||||
:align: center
|
||||
|
||||
:attr:`anchor_title` is an :class:`~kivy.properties.OptionProperty`
|
||||
and defaults to `'left'`.
|
||||
"""
|
||||
|
||||
padding = ListProperty([0, 0, 0, 0])
|
||||
"""
|
||||
Padding for contents of the front layer.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-padding.png
|
||||
:align: center
|
||||
|
||||
:attr:`padding` is an :class:`~kivy.properties.ListProperty`
|
||||
and defaults to `[0, 0, 0, 0]`.
|
||||
"""
|
||||
|
||||
left_action_items = ListProperty()
|
||||
"""
|
||||
The icons and methods left of the :class:`kivymd.uix.toolbar.MDTopAppBar`
|
||||
in back layer. For more information, see the
|
||||
:class:`kivymd.uix.toolbar.MDTopAppBar` module
|
||||
and :attr:`left_action_items` parameter.
|
||||
|
||||
:attr:`left_action_items` is an :class:`~kivy.properties.ListProperty`
|
||||
and defaults to `[]`.
|
||||
"""
|
||||
|
||||
right_action_items = ListProperty()
|
||||
"""
|
||||
Works the same way as :attr:`left_action_items`.
|
||||
|
||||
:attr:`right_action_items` is an :class:`~kivy.properties.ListProperty`
|
||||
and defaults to `[]`.
|
||||
"""
|
||||
|
||||
title = StringProperty()
|
||||
"""
|
||||
See the :class:`kivymd.uix.toolbar.MDTopAppBar.title` parameter.
|
||||
|
||||
:attr:`title` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
back_layer_color = ColorProperty(None)
|
||||
"""
|
||||
Background color of back layer in (r, g, b, a) or string format.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-back-layer-color.png
|
||||
:align: center
|
||||
|
||||
:attr:`back_layer_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
front_layer_color = ColorProperty(None)
|
||||
"""
|
||||
Background color of front layer in (r, g, b, a) or string format.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-front-layer-color.png
|
||||
:align: center
|
||||
|
||||
:attr:`front_layer_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
radius_left = NumericProperty("16dp")
|
||||
"""
|
||||
The value of the rounding radius of the upper left corner
|
||||
of the front layer.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-radius-left.png
|
||||
:align: center
|
||||
|
||||
:attr:`radius_left` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `16dp`.
|
||||
"""
|
||||
|
||||
radius_right = NumericProperty("16dp")
|
||||
"""
|
||||
The value of the rounding radius of the upper right corner
|
||||
of the front layer.
|
||||
|
||||
:attr:`radius_right` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `16dp`.
|
||||
"""
|
||||
|
||||
header = BooleanProperty(True)
|
||||
"""
|
||||
Whether to use a header above the contents of the front layer.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-header.png
|
||||
:align: center
|
||||
|
||||
:attr:`header` is an :class:`~kivy.properties.BooleanProperty`
|
||||
and defaults to `True`.
|
||||
"""
|
||||
|
||||
header_text = StringProperty("Header")
|
||||
"""
|
||||
Text of header.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-header-text.png
|
||||
:align: center
|
||||
|
||||
:attr:`header_text` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'Header'`.
|
||||
"""
|
||||
|
||||
close_icon = StringProperty("close")
|
||||
"""
|
||||
The name of the icon that will be installed on the toolbar
|
||||
on the left when opening the front layer.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-close-icon.png
|
||||
:align: center
|
||||
|
||||
:attr:`close_icon` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'close'`.
|
||||
"""
|
||||
|
||||
opening_time = NumericProperty(0.2)
|
||||
"""
|
||||
The time taken for the panel to slide to the :attr:`state` `'open'`.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`opening_time` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.2`.
|
||||
"""
|
||||
|
||||
opening_transition = StringProperty("out_quad")
|
||||
"""
|
||||
The name of the animation transition type to use when animating to
|
||||
the :attr:`state` `'open'`.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`opening_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'out_quad'`.
|
||||
"""
|
||||
|
||||
closing_time = NumericProperty(0.2)
|
||||
"""
|
||||
The time taken for the panel to slide to the :attr:`state` `'close'`.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`closing_time` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.2`.
|
||||
"""
|
||||
|
||||
closing_transition = StringProperty("out_quad")
|
||||
"""
|
||||
The name of the animation transition type to use when animating to
|
||||
the :attr:`state` 'close'.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`closing_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'out_quad'`.
|
||||
"""
|
||||
|
||||
_open_icon = ""
|
||||
_front_layer_open = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.register_event_type("on_open")
|
||||
self.register_event_type("on_close")
|
||||
Clock.schedule_once(
|
||||
lambda x: self.on_left_action_items(self, self.left_action_items)
|
||||
)
|
||||
|
||||
def on_open(self) -> None:
|
||||
"""When the front layer drops."""
|
||||
|
||||
def on_close(self) -> None:
|
||||
"""When the front layer rises."""
|
||||
|
||||
def on_left_action_items(self, instance_backdrop, menu: list) -> None:
|
||||
if menu:
|
||||
self.left_action_items = [menu[0]]
|
||||
else:
|
||||
self.left_action_items = [["menu", lambda x: self.open()]]
|
||||
self._open_icon = self.left_action_items[0][0]
|
||||
|
||||
def on_header(self, instance_backdrop, value: bool) -> None:
|
||||
def on_header(*args):
|
||||
if not value:
|
||||
self.ids._front_layer.remove_widget(self.ids.header_button)
|
||||
|
||||
Clock.schedule_once(on_header)
|
||||
|
||||
def open(self, open_up_to: int = 0) -> None:
|
||||
"""
|
||||
Opens the front layer.
|
||||
|
||||
:open_up_to:
|
||||
the height to which the front screen will be lowered;
|
||||
if equal to zero - falls to the bottom of the screen;
|
||||
"""
|
||||
|
||||
self.animate_opacity_icon()
|
||||
if self._front_layer_open:
|
||||
self.close()
|
||||
return
|
||||
|
||||
if open_up_to:
|
||||
if open_up_to < (
|
||||
self.ids.header_button.height - self.ids._front_layer.height
|
||||
):
|
||||
y = self.ids.header_button.height - self.ids._front_layer.height
|
||||
elif open_up_to > 0:
|
||||
y = 0
|
||||
else:
|
||||
y = open_up_to
|
||||
else:
|
||||
y = self.ids.header_button.height - self.ids._front_layer.height
|
||||
|
||||
Animation(y=y, d=self.opening_time, t=self.opening_transition).start(
|
||||
self.ids._front_layer
|
||||
)
|
||||
self._front_layer_open = True
|
||||
self.dispatch("on_open")
|
||||
|
||||
def close(self) -> None:
|
||||
"""Opens the front layer."""
|
||||
|
||||
Animation(y=0, d=self.closing_time, t=self.closing_transition).start(
|
||||
self.ids._front_layer
|
||||
)
|
||||
self._front_layer_open = False
|
||||
self.dispatch("on_close")
|
||||
|
||||
def animate_opacity_icon(
|
||||
self,
|
||||
instance_icon_menu: Union[ActionTopAppBarButton, None] = None,
|
||||
opacity_value: int = 0,
|
||||
call_set_new_icon: bool = True,
|
||||
) -> None:
|
||||
"""Starts the opacity animation of the icon."""
|
||||
|
||||
if not instance_icon_menu:
|
||||
instance_icon_menu = self.ids.toolbar.ids.left_actions.children[0]
|
||||
anim = Animation(
|
||||
opacity=opacity_value,
|
||||
d=self.opening_time,
|
||||
t=self.opening_transition,
|
||||
)
|
||||
if call_set_new_icon:
|
||||
anim.bind(on_complete=self.set_new_icon)
|
||||
anim.start(instance_icon_menu)
|
||||
|
||||
def set_new_icon(
|
||||
self,
|
||||
instance_animation: Animation,
|
||||
instance_icon_menu: ActionTopAppBarButton,
|
||||
) -> None:
|
||||
"""
|
||||
Sets the icon of the button depending on the state of the backdrop.
|
||||
"""
|
||||
|
||||
instance_icon_menu.icon = (
|
||||
self.close_icon
|
||||
if instance_icon_menu.icon == self._open_icon
|
||||
else self._open_icon
|
||||
)
|
||||
self.animate_opacity_icon(instance_icon_menu, 1, False)
|
||||
|
||||
def add_widget(self, widget, index=0, canvas=None):
|
||||
if widget.__class__ in (MDBackdropToolbar, _BackLayer, _FrontLayer):
|
||||
return super().add_widget(widget)
|
||||
else:
|
||||
if widget.__class__ is MDBackdropBackLayer:
|
||||
self.ids.back_layer.add_widget(widget)
|
||||
elif widget.__class__ is MDBackdropFrontLayer:
|
||||
self.ids.front_layer.add_widget(widget)
|
||||
|
||||
|
||||
class MDBackdropToolbar(MDTopAppBar):
|
||||
"""
|
||||
Implements a toolbar for back content.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.toolbar.toolbar.MDTopAppBar` classes documentation.
|
||||
"""
|
||||
|
||||
|
||||
class MDBackdropFrontLayer(MDBoxLayout):
|
||||
"""
|
||||
Container for front content.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.boxlayout.MDBoxLayout` classes documentation.
|
||||
"""
|
||||
|
||||
|
||||
class MDBackdropBackLayer(MDBoxLayout):
|
||||
"""
|
||||
Container for back content.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
|
||||
"""
|
||||
|
||||
|
||||
class _BackLayer(BoxLayout):
|
||||
pass
|
||||
|
||||
|
||||
class _FrontLayer(MDCard):
|
||||
pass
|
|
@ -0,0 +1 @@
|
|||
from .badge import MDBadge # NOQA F401
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,19 @@
|
|||
<MDBadge>
|
||||
font_style: "Label"
|
||||
role: "small"
|
||||
radius: [self.texture_size[1] / 2, ]
|
||||
pos_hint: {"center_x": 0.5, "center_y": 0.5}
|
||||
padding: "4dp", "2dp"
|
||||
halign: "center"
|
||||
valign: "center"
|
||||
adaptive_size: True
|
||||
md_bg_color: self.theme_cls.errorColor
|
||||
text_color: self.theme_cls.onErrorColor
|
||||
size_hint: None, None
|
||||
size: self.texture_size
|
||||
pos:
|
||||
( \
|
||||
self.parent.x + (self.parent.width / 2), \
|
||||
self.parent.y + (self.parent.height / 2) \
|
||||
) \
|
||||
if self.parent else (0, 0)
|
|
@ -0,0 +1,74 @@
|
|||
"""
|
||||
Components/Badge
|
||||
================
|
||||
|
||||
.. versionadded:: 2.0.0
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Material Design 3 spec, Badge <https://m3.material.io/components/badges/overview>`_
|
||||
|
||||
.. rubric:: Badges show notifications, counts, or status information on
|
||||
navigation items and icons.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/badges.png
|
||||
:align: center
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from kivy.lang import Builder
|
||||
|
||||
from kivymd.app import MDApp
|
||||
|
||||
KV = '''
|
||||
MDScreen:
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
|
||||
MDIcon:
|
||||
icon: "gmail"
|
||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
||||
|
||||
MDBadge:
|
||||
text: "12"
|
||||
'''
|
||||
|
||||
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
return Builder.load_string(KV)
|
||||
|
||||
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/badges-example.png
|
||||
:align: center
|
||||
"""
|
||||
|
||||
__all__ = ("MDBadge",)
|
||||
|
||||
import os
|
||||
|
||||
from kivy.lang import Builder
|
||||
|
||||
from kivymd.uix.label import MDLabel
|
||||
from kivymd import uix_path
|
||||
|
||||
with open(
|
||||
os.path.join(uix_path, "badge", "badge.kv"), encoding="utf-8"
|
||||
) as kv_file:
|
||||
Builder.load_string(kv_file.read())
|
||||
|
||||
|
||||
class MDBadge(MDLabel):
|
||||
"""
|
||||
Badge class.
|
||||
|
||||
.. versionadded:: 2.0.0
|
||||
|
||||
For more information see in the
|
||||
:class:`~kivymd.uix.label.label.MDLabel` class documentation.
|
||||
"""
|
|
@ -1 +0,0 @@
|
|||
from .banner import MDBanner # NOQA F401
|
Binary file not shown.
Binary file not shown.
|
@ -1,85 +0,0 @@
|
|||
#:import Window kivy.core.window.Window
|
||||
|
||||
|
||||
<ThreeLineIconBanner>
|
||||
text: root.text_message[0]
|
||||
secondary_text: root.text_message[1]
|
||||
tertiary_text: root.text_message[2]
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
ImageLeftWidget:
|
||||
source: root.icon
|
||||
|
||||
|
||||
<TwoLineIconBanner>
|
||||
text: root.text_message[0]
|
||||
secondary_text: root.text_message[1]
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
ImageLeftWidget:
|
||||
source: root.icon
|
||||
|
||||
|
||||
<OneLineIconBanner>
|
||||
text: root.text_message[0]
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
ImageLeftWidget:
|
||||
source: root.icon
|
||||
|
||||
|
||||
<ThreeLineBanner>
|
||||
text: root.text_message[0]
|
||||
secondary_text: root.text_message[1]
|
||||
tertiary_text: root.text_message[2]
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
|
||||
<TwoLineBanner>
|
||||
text: root.text_message[0]
|
||||
secondary_text: root.text_message[1]
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
|
||||
<OneLineBanner>
|
||||
text: root.text_message[0]
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
|
||||
<MDBanner>
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
banner_y: 0
|
||||
orientation: "vertical"
|
||||
y: Window.height - self.banner_y
|
||||
|
||||
canvas:
|
||||
Color:
|
||||
rgba: 0, 0, 0, 0
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
|
||||
MDBoxLayout:
|
||||
id: container_message
|
||||
adaptive_height: True
|
||||
|
||||
MDBoxLayout:
|
||||
adaptive_size: True
|
||||
pos_hint: {"right": 1}
|
||||
padding: 0, 0, "8dp", "8dp"
|
||||
spacing: "8dp"
|
||||
|
||||
MDBoxLayout:
|
||||
id: left_action_box
|
||||
adaptive_size: True
|
||||
|
||||
MDBoxLayout:
|
||||
id: right_action_box
|
||||
adaptive_size: True
|
|
@ -1,439 +0,0 @@
|
|||
"""
|
||||
Components/Banner
|
||||
=================
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Material Design spec, Banner <https://material.io/components/banners>`_
|
||||
|
||||
.. rubric:: A banner displays a prominent message and related optional actions.
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/banner.png
|
||||
:align: center
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from kivy.lang import Builder
|
||||
from kivy.factory import Factory
|
||||
|
||||
from kivymd.app import MDApp
|
||||
|
||||
Builder.load_string('''
|
||||
<ExampleBanner@Screen>
|
||||
|
||||
MDBanner:
|
||||
id: banner
|
||||
text: ["One line string text example without actions."]
|
||||
# The widget that is under the banner.
|
||||
# It will be shifted down to the height of the banner.
|
||||
over_widget: screen
|
||||
vertical_pad: toolbar.height
|
||||
|
||||
MDTopAppBar:
|
||||
id: toolbar
|
||||
title: "Example Banners"
|
||||
elevation: 4
|
||||
pos_hint: {'top': 1}
|
||||
|
||||
MDBoxLayout:
|
||||
id: screen
|
||||
orientation: "vertical"
|
||||
size_hint_y: None
|
||||
height: Window.height - toolbar.height
|
||||
|
||||
OneLineListItem:
|
||||
text: "Banner without actions"
|
||||
on_release: banner.show()
|
||||
|
||||
Widget:
|
||||
''')
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
def build(self):
|
||||
return Factory.ExampleBanner()
|
||||
|
||||
|
||||
Test().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/banner-example-1.gif
|
||||
:align: center
|
||||
|
||||
.. rubric:: Banner type.
|
||||
|
||||
By default, the banner is of the type ``'one-line'``:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBanner:
|
||||
text: ["One line string text example without actions."]
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/banner-one-line.png
|
||||
:align: center
|
||||
|
||||
To use a two-line banner, specify the ``'two-line'`` :attr:`MDBanner.type` for the banner
|
||||
and pass the list of two lines to the :attr:`MDBanner.text` parameter:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBanner:
|
||||
type: "two-line"
|
||||
text:
|
||||
["One line string text example without actions.", "This is the second line of the banner message."]
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/banner-two-line.png
|
||||
:align: center
|
||||
|
||||
Similarly, create a three-line banner:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBanner:
|
||||
type: "three-line"
|
||||
text:
|
||||
["One line string text example without actions.", "This is the second line of the banner message.", "and this is the third line of the banner message."]
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/banner-three-line.png
|
||||
:align: center
|
||||
|
||||
To add buttons to any type of banner,
|
||||
use the :attr:`MDBanner.left_action` and :attr:`MDBanner.right_action` parameters,
|
||||
which should take a list ['Button name', function]:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBanner:
|
||||
text: ["One line string text example without actions."]
|
||||
left_action: ["CANCEL", lambda x: None]
|
||||
|
||||
Or two buttons:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBanner:
|
||||
text: ["One line string text example without actions."]
|
||||
left_action: ["CANCEL", lambda x: None]
|
||||
right_action: ["CLOSE", lambda x: None]
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/banner-actions.png
|
||||
:align: center
|
||||
|
||||
If you want to use the icon on the left in the banner,
|
||||
add the prefix `'-icon'` to the banner type:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBanner:
|
||||
type: "one-line-icon"
|
||||
icon: f"{images_path}/kivymd.png"
|
||||
text: ["One line string text example without actions."]
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/banner-icon.png
|
||||
:align: center
|
||||
|
||||
.. Note:: `See full example <https://github.com/kivymd/KivyMD/wiki/Components-Banner>`_
|
||||
"""
|
||||
|
||||
__all__ = ("MDBanner",)
|
||||
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.clock import Clock
|
||||
from kivy.lang import Builder
|
||||
from kivy.metrics import dp
|
||||
from kivy.properties import (
|
||||
BoundedNumericProperty,
|
||||
ListProperty,
|
||||
NumericProperty,
|
||||
ObjectProperty,
|
||||
OptionProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from kivy.uix.widget import Widget
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.button import MDFlatButton
|
||||
from kivymd.uix.card import MDCard
|
||||
from kivymd.uix.list import (
|
||||
OneLineAvatarListItem,
|
||||
OneLineListItem,
|
||||
ThreeLineAvatarListItem,
|
||||
ThreeLineListItem,
|
||||
TwoLineAvatarListItem,
|
||||
TwoLineListItem,
|
||||
)
|
||||
|
||||
with open(
|
||||
os.path.join(uix_path, "banner", "banner.kv"),
|
||||
encoding="utf-8",
|
||||
) as kv_file:
|
||||
Builder.load_string(kv_file.read())
|
||||
|
||||
|
||||
class MDBanner(MDCard):
|
||||
"""
|
||||
Banner class.
|
||||
|
||||
For more information, see in the :class:`~kivymd.uix.card.MDCard`
|
||||
class documentation.
|
||||
"""
|
||||
|
||||
vertical_pad = NumericProperty(dp(68))
|
||||
"""
|
||||
Indent the banner at the top of the screen.
|
||||
|
||||
:attr:`vertical_pad` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `dp(68)`.
|
||||
"""
|
||||
|
||||
opening_transition = StringProperty("in_quad")
|
||||
"""
|
||||
The name of the animation transition.
|
||||
|
||||
:attr:`opening_transition` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'in_quad'`.
|
||||
"""
|
||||
|
||||
icon = StringProperty("data/logo/kivy-icon-128.png")
|
||||
"""
|
||||
Icon banner.
|
||||
|
||||
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'data/logo/kivy-icon-128.png'`.
|
||||
"""
|
||||
|
||||
over_widget = ObjectProperty()
|
||||
"""
|
||||
The widget that is under the banner.
|
||||
It will be shifted down to the height of the banner.
|
||||
|
||||
:attr:`over_widget` is an :class:`~kivy.properties.ObjectProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
text = ListProperty()
|
||||
"""
|
||||
List of lines for banner text.
|
||||
Must contain no more than three lines for a
|
||||
`'one-line'`, `'two-line'` and `'three-line'` banner, respectively.
|
||||
|
||||
:attr:`text` is an :class:`~kivy.properties.ListProperty`
|
||||
and defaults to `[]`.
|
||||
"""
|
||||
|
||||
left_action = ListProperty()
|
||||
"""
|
||||
The action of banner.
|
||||
|
||||
To add one action, make a list [`'name_action'`, callback]
|
||||
where `'name_action'` is a string that corresponds to an action name and
|
||||
``callback`` is the function called on a touch release event.
|
||||
|
||||
:attr:`left_action` is an :class:`~kivy.properties.ListProperty`
|
||||
and defaults to `[]`.
|
||||
"""
|
||||
|
||||
right_action = ListProperty()
|
||||
"""
|
||||
Works the same way as :attr:`left_action`.
|
||||
|
||||
:attr:`right_action` is an :class:`~kivy.properties.ListProperty`
|
||||
and defaults to `[]`.
|
||||
"""
|
||||
|
||||
type = OptionProperty(
|
||||
"one-line",
|
||||
options=[
|
||||
"one-line",
|
||||
"two-line",
|
||||
"three-line",
|
||||
"one-line-icon",
|
||||
"two-line-icon",
|
||||
"three-line-icon",
|
||||
],
|
||||
allownone=True,
|
||||
)
|
||||
"""
|
||||
Banner type. . Available options are: (`"one-line"`, `"two-line"`,
|
||||
`"three-line"`, `"one-line-icon"`, `"two-line-icon"`, `"three-line-icon"`).
|
||||
|
||||
:attr:`type` is an :class:`~kivy.properties.OptionProperty`
|
||||
and defaults to `'one-line'`.
|
||||
"""
|
||||
|
||||
opening_timeout = BoundedNumericProperty(0.7, min=0.7)
|
||||
"""
|
||||
Time interval after which the banner will be shown.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`opening_timeout` is an :class:`~kivy.properties.BoundedNumericProperty`
|
||||
and defaults to `0.7`.
|
||||
"""
|
||||
|
||||
opening_time = NumericProperty(0.15)
|
||||
"""
|
||||
The time taken for the banner to slide to the :attr:`state` `'open'`.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`opening_time` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.15`.
|
||||
"""
|
||||
|
||||
closing_time = NumericProperty(0.15)
|
||||
"""
|
||||
The time taken for the banner to slide to the :attr:`state` `'close'`.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`closing_time` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.15`.
|
||||
"""
|
||||
|
||||
_type_message = None
|
||||
_progress = False
|
||||
|
||||
def add_actions_buttons(
|
||||
self, instance_box: MDBoxLayout, data: list
|
||||
) -> None:
|
||||
"""
|
||||
Adds buttons to the banner.
|
||||
|
||||
:param data: ['NAME BUTTON', <function>];
|
||||
"""
|
||||
|
||||
if data:
|
||||
name_action_button, function_action_button = data
|
||||
action_button = MDFlatButton(
|
||||
text=f"[b]{name_action_button}[/b]",
|
||||
theme_text_color="Custom",
|
||||
text_color=self.theme_cls.primary_color,
|
||||
on_release=function_action_button,
|
||||
)
|
||||
action_button.markup = True
|
||||
instance_box.add_widget(action_button)
|
||||
|
||||
def show(self) -> None:
|
||||
"""Displays a banner on the screen."""
|
||||
|
||||
def show(interval: Union[int, float]):
|
||||
self.set_type_banner()
|
||||
self.add_actions_buttons(self.ids.left_action_box, self.left_action)
|
||||
self.add_actions_buttons(
|
||||
self.ids.right_action_box, self.right_action
|
||||
)
|
||||
self._add_banner_to_container()
|
||||
Clock.schedule_once(self.animation_display_banner, 0.1)
|
||||
|
||||
if not self._progress:
|
||||
self._progress = True
|
||||
if self.ids.container_message.children:
|
||||
self.hide()
|
||||
Clock.schedule_once(show, self.opening_timeout)
|
||||
|
||||
def hide(self) -> None:
|
||||
"""Hides the banner from the screen."""
|
||||
|
||||
def hide(interval: Union[int, float]):
|
||||
anim = Animation(banner_y=0, d=self.closing_time)
|
||||
anim.bind(on_complete=self._remove_banner)
|
||||
anim.start(self)
|
||||
Animation(
|
||||
y=self.over_widget.y + self.height, d=self.closing_time
|
||||
).start(self.over_widget)
|
||||
|
||||
if not self._progress:
|
||||
self._progress = True
|
||||
Clock.schedule_once(hide, 0.5)
|
||||
|
||||
def set_type_banner(self) -> None:
|
||||
self._type_message = {
|
||||
"three-line-icon": ThreeLineIconBanner,
|
||||
"two-line-icon": TwoLineIconBanner,
|
||||
"one-line-icon": OneLineIconBanner,
|
||||
"three-line": ThreeLineBanner,
|
||||
"two-line": TwoLineBanner,
|
||||
"one-line": OneLineBanner,
|
||||
}[self.type]
|
||||
|
||||
def animation_display_banner(self, interval: Union[int, float]) -> None:
|
||||
Animation(
|
||||
banner_y=self.height + self.vertical_pad,
|
||||
d=self.opening_time,
|
||||
t=self.opening_transition,
|
||||
).start(self)
|
||||
anim = Animation(
|
||||
y=self.over_widget.y - self.height,
|
||||
d=self.opening_time,
|
||||
t=self.opening_transition,
|
||||
)
|
||||
anim.bind(on_complete=self._reset_progress)
|
||||
anim.start(self.over_widget)
|
||||
|
||||
def _remove_banner(self, *args):
|
||||
self.ids.container_message.clear_widgets()
|
||||
self.ids.left_action_box.clear_widgets()
|
||||
self.ids.right_action_box.clear_widgets()
|
||||
self._reset_progress()
|
||||
|
||||
def _reset_progress(self, *args):
|
||||
self._progress = False
|
||||
|
||||
def _add_banner_to_container(self) -> None:
|
||||
self.ids.container_message.add_widget(
|
||||
self._type_message(text_message=self.text, icon=self.icon)
|
||||
)
|
||||
|
||||
|
||||
class BaseBanner(Widget):
|
||||
"""Implements the base banner class."""
|
||||
|
||||
text_message = ListProperty(["", "", ""])
|
||||
"""
|
||||
List of banner strings. First, second and, respectively, third lines.
|
||||
|
||||
:attr:`text_message` is an :class:`~kivy.properties.ListProperty`
|
||||
and defaults to `['', '', '']`.
|
||||
"""
|
||||
|
||||
icon = StringProperty()
|
||||
"""
|
||||
Icon banner.
|
||||
|
||||
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
self.parent.parent.hide()
|
||||
|
||||
|
||||
class ThreeLineIconBanner(ThreeLineAvatarListItem, BaseBanner):
|
||||
pass
|
||||
|
||||
|
||||
class TwoLineIconBanner(TwoLineAvatarListItem, BaseBanner):
|
||||
pass
|
||||
|
||||
|
||||
class OneLineIconBanner(OneLineAvatarListItem, BaseBanner):
|
||||
pass
|
||||
|
||||
|
||||
class ThreeLineBanner(ThreeLineListItem, BaseBanner):
|
||||
pass
|
||||
|
||||
|
||||
class TwoLineBanner(TwoLineListItem, BaseBanner):
|
||||
pass
|
||||
|
||||
|
||||
class OneLineBanner(OneLineListItem, BaseBanner):
|
||||
pass
|
|
@ -5,21 +5,11 @@ Behaviors
|
|||
Modules and classes implementing various behaviors for buttons etc.
|
||||
"""
|
||||
|
||||
from .backgroundcolor_behavior import (
|
||||
BackgroundColorBehavior,
|
||||
SpecificBackgroundColorBehavior,
|
||||
)
|
||||
from .backgroundcolor_behavior import BackgroundColorBehavior
|
||||
|
||||
# flake8: NOQA
|
||||
from .declarative_behavior import DeclarativeBehavior
|
||||
from .elevation import (
|
||||
CircularElevationBehavior,
|
||||
CommonElevationBehavior,
|
||||
FakeCircularElevationBehavior,
|
||||
FakeRectangularElevationBehavior,
|
||||
RectangularElevationBehavior,
|
||||
RoundedRectangularElevationBehavior,
|
||||
)
|
||||
from .elevation import CommonElevationBehavior
|
||||
from .motion_behavior import (
|
||||
MotionDialogBehavior,
|
||||
MotionShackBehavior,
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -7,7 +7,7 @@ Behaviors/Background Color
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = ("BackgroundColorBehavior", "SpecificBackgroundColorBehavior")
|
||||
__all__ = ("BackgroundColorBehavior",)
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.lang import Builder
|
||||
|
@ -15,15 +15,10 @@ from kivy.properties import (
|
|||
ColorProperty,
|
||||
ListProperty,
|
||||
NumericProperty,
|
||||
OptionProperty,
|
||||
ReferenceListProperty,
|
||||
StringProperty,
|
||||
VariableListProperty,
|
||||
)
|
||||
from kivy.utils import get_color_from_hex
|
||||
|
||||
from kivymd.color_definitions import hue, palette, text_colors
|
||||
from kivymd.theming import ThemeManager
|
||||
|
||||
Builder.load_string(
|
||||
"""
|
||||
|
@ -37,8 +32,9 @@ Builder.load_string(
|
|||
angle: self.angle
|
||||
origin: self._background_origin
|
||||
Color:
|
||||
group: "backgroundcolor-behavior-bg-color"
|
||||
rgba: self._md_bg_color
|
||||
RoundedRectangle:
|
||||
SmoothRoundedRectangle:
|
||||
group: "Background_instruction"
|
||||
size: self.size
|
||||
pos: self.pos if not isinstance(self, RelativeLayout) else (0, 0)
|
||||
|
@ -49,11 +45,17 @@ Builder.load_string(
|
|||
source: root.background
|
||||
Color:
|
||||
rgba: self.line_color if self.line_color else (0, 0, 0, 0)
|
||||
# TODO: maybe we should use SmoothLine,
|
||||
# but this should be tested on all widgets.
|
||||
Line:
|
||||
SmoothLine:
|
||||
width: root.line_width
|
||||
rounded_rectangle:
|
||||
[ \
|
||||
0,
|
||||
0, \
|
||||
self.width, \
|
||||
self.height, \
|
||||
*self.radius, \
|
||||
] \
|
||||
if isinstance(self, RelativeLayout) else \
|
||||
[ \
|
||||
self.x,
|
||||
self.y, \
|
||||
|
@ -73,7 +75,7 @@ class BackgroundColorBehavior:
|
|||
Background image path.
|
||||
|
||||
:attr:`background` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `None`.
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
radius = VariableListProperty([0], length=4)
|
||||
|
@ -93,7 +95,7 @@ class BackgroundColorBehavior:
|
|||
|
||||
# FIXME: in this case, we will not be able to animate this property
|
||||
# using the `Animation` class.
|
||||
md_bg_color = ColorProperty([1, 1, 1, 0])
|
||||
md_bg_color = ColorProperty([0, 0, 0, 0])
|
||||
"""
|
||||
The background color of the widget (:class:`~kivy.uix.widget.Widget`)
|
||||
that will be inherited from the :attr:`BackgroundColorBehavior` class.
|
||||
|
@ -118,12 +120,12 @@ class BackgroundColorBehavior:
|
|||
md_bg_color: 0, 1, 1, 1
|
||||
|
||||
:attr:`md_bg_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[1, 1, 1, 0]`.
|
||||
and defaults to `[0, 0, 0, 0]`.
|
||||
"""
|
||||
|
||||
line_color = ColorProperty([0, 0, 0, 0])
|
||||
"""
|
||||
If a custom value is specified for the `line_color parameter`, the border
|
||||
If a custom value is specified for the `line_color` parameter, the border
|
||||
of the specified color will be used to border the widget:
|
||||
|
||||
.. code-block:: kv
|
||||
|
@ -157,37 +159,18 @@ class BackgroundColorBehavior:
|
|||
_background_y = NumericProperty(0)
|
||||
_background_origin = ReferenceListProperty(_background_x, _background_y)
|
||||
_md_bg_color = ColorProperty([0, 0, 0, 0])
|
||||
_origin_line_color = ColorProperty(None)
|
||||
_origin_md_bg_color = ColorProperty(None)
|
||||
|
||||
def __init__(self, **kwarg):
|
||||
super().__init__(**kwarg)
|
||||
self.bind(
|
||||
pos=self.update_background_origin,
|
||||
disabled=self.restore_color_origin,
|
||||
)
|
||||
self.bind(pos=self.update_background_origin)
|
||||
|
||||
def restore_color_origin(self, instance_md_widget, value: bool) -> None:
|
||||
"""Called when the values of :attr:`disabled` change."""
|
||||
|
||||
if not value:
|
||||
if self._origin_line_color:
|
||||
self.line_color = self._origin_line_color
|
||||
if self._origin_md_bg_color:
|
||||
self.md_bg_color = self._origin_md_bg_color
|
||||
|
||||
def on_line_color(self, instance_md_widget, value: list | str) -> None:
|
||||
"""Called when the values of :attr:`line_color` change."""
|
||||
|
||||
if not self.disabled:
|
||||
self._origin_line_color = value
|
||||
|
||||
def on_md_bg_color(self, instance_md_widget, color: list | str):
|
||||
"""Called when the values of :attr:`md_bg_color` change."""
|
||||
def on_md_bg_color(self, instance, color: list | str):
|
||||
"""Fired when the values of :attr:`md_bg_color` change."""
|
||||
|
||||
if (
|
||||
hasattr(self, "theme_cls")
|
||||
and self.theme_cls.theme_style_switch_animation
|
||||
and self.__class__.__name__ != "MDDropdownMenu"
|
||||
):
|
||||
Animation(
|
||||
_md_bg_color=color,
|
||||
|
@ -197,94 +180,10 @@ class BackgroundColorBehavior:
|
|||
else:
|
||||
self._md_bg_color = color
|
||||
|
||||
if not self.disabled:
|
||||
self._origin_md_bg_color = color
|
||||
|
||||
def update_background_origin(self, instance_md_widget, pos: list) -> None:
|
||||
"""Called when the values of :attr:`pos` change."""
|
||||
def update_background_origin(self, instance, pos: list) -> None:
|
||||
"""Fired when the values of :attr:`pos` change."""
|
||||
|
||||
if self.background_origin:
|
||||
self._background_origin = self.background_origin
|
||||
else:
|
||||
self._background_origin = self.center
|
||||
|
||||
|
||||
class SpecificBackgroundColorBehavior(BackgroundColorBehavior):
|
||||
background_palette = OptionProperty(
|
||||
"Primary", options=["Primary", "Accent", *palette]
|
||||
)
|
||||
"""
|
||||
See :attr:`kivymd.color_definitions.palette`.
|
||||
|
||||
:attr:`background_palette` is an :class:`~kivy.properties.OptionProperty`
|
||||
and defaults to `'Primary'`.
|
||||
"""
|
||||
|
||||
background_hue = OptionProperty("500", options=hue)
|
||||
"""
|
||||
See :attr:`kivymd.color_definitions.hue`.
|
||||
|
||||
:attr:`background_hue` is an :class:`~kivy.properties.OptionProperty`
|
||||
and defaults to `'500'`.
|
||||
"""
|
||||
|
||||
specific_text_color = ColorProperty([0, 0, 0, 0.87])
|
||||
"""
|
||||
:attr:`specific_text_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[0, 0, 0, 0.87]`.
|
||||
"""
|
||||
|
||||
specific_secondary_text_color = ColorProperty([0, 0, 0, 0.87])
|
||||
"""
|
||||
:attr:`specific_secondary_text_color`is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[0, 0, 0, 0.87]`.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if hasattr(self, "theme_cls"):
|
||||
self.theme_cls.bind(
|
||||
primary_palette=self._update_specific_text_color,
|
||||
accent_palette=self._update_specific_text_color,
|
||||
theme_style=self._update_specific_text_color,
|
||||
)
|
||||
self.bind(
|
||||
background_hue=self._update_specific_text_color,
|
||||
background_palette=self._update_specific_text_color,
|
||||
)
|
||||
self._update_specific_text_color(None, None)
|
||||
|
||||
def _update_specific_text_color(
|
||||
self, instance_theme_manager: ThemeManager, theme_style: str
|
||||
) -> None:
|
||||
if hasattr(self, "theme_cls"):
|
||||
palette = {
|
||||
"Primary": self.theme_cls.primary_palette,
|
||||
"Accent": self.theme_cls.accent_palette,
|
||||
}.get(self.background_palette, self.background_palette)
|
||||
else:
|
||||
palette = {"Primary": "Blue", "Accent": "Amber"}.get(
|
||||
self.background_palette, self.background_palette
|
||||
)
|
||||
color = get_color_from_hex(text_colors[palette][self.background_hue])
|
||||
secondary_color = color[:]
|
||||
# Check for black text (need to adjust opacity).
|
||||
if (color[0] + color[1] + color[2]) == 0:
|
||||
color[3] = 0.87
|
||||
secondary_color[3] = 0.54
|
||||
else:
|
||||
secondary_color[3] = 0.7
|
||||
|
||||
if (
|
||||
hasattr(self, "theme_cls")
|
||||
and self.theme_cls.theme_style_switch_animation
|
||||
):
|
||||
Animation(
|
||||
specific_text_color=color,
|
||||
specific_secondary_text_color=secondary_color,
|
||||
d=self.theme_cls.theme_style_switch_animation_duration,
|
||||
t="linear",
|
||||
).start(self)
|
||||
else:
|
||||
self.specific_text_color = color
|
||||
self.specific_secondary_text_color = secondary_color
|
||||
|
|
|
@ -33,31 +33,35 @@ Imperative style
|
|||
.. code-block:: python
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
|
||||
from kivymd.uix.label import MDLabel
|
||||
from kivymd.uix.navigationbar import (
|
||||
MDNavigationBar,
|
||||
MDNavigationItem,
|
||||
MDNavigationItemIcon,
|
||||
MDNavigationItemLabel,
|
||||
)
|
||||
from kivymd.uix.screen import MDScreen
|
||||
|
||||
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
screen = MDScreen()
|
||||
bottom_navigation = MDBottomNavigation(
|
||||
panel_color="#eeeaea",
|
||||
selected_color_background="#97ecf8",
|
||||
text_color_active="white",
|
||||
)
|
||||
bottom_navigation = MDNavigationBar()
|
||||
|
||||
data = {
|
||||
"screen 1": {"text": "Mail", "icon": "gmail"},
|
||||
"screen 2": {"text": "Discord", "icon": "discord"},
|
||||
"screen 3": {"text": "LinkedIN", "icon": "linkedin"},
|
||||
}
|
||||
for key in data.keys():
|
||||
text = data[key]["text"]
|
||||
navigation_item = MDBottomNavigationItem(
|
||||
name=key, text=text, icon=data[key]["icon"]
|
||||
datas = [
|
||||
{"text": "Mail", "icon": "gmail"},
|
||||
{"text": "GitHub", "icon": "git"},
|
||||
{"text": "LinkedIN", "icon": "linkedin"},
|
||||
]
|
||||
for data in datas:
|
||||
text = data["text"]
|
||||
navigation_item = MDNavigationItem(
|
||||
MDNavigationItemIcon(
|
||||
icon=data["icon"],
|
||||
),
|
||||
MDNavigationItemLabel(
|
||||
text=text,
|
||||
),
|
||||
)
|
||||
navigation_item.add_widget(MDLabel(text=text, halign="center"))
|
||||
bottom_navigation.add_widget(navigation_item)
|
||||
|
||||
screen.add_widget(bottom_navigation)
|
||||
|
@ -66,9 +70,6 @@ Imperative style
|
|||
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-styles-programming.png
|
||||
:align: center
|
||||
|
||||
Take a look at the above code example. This is a very simple UI. But looking
|
||||
at this code, you will not be able to figure the widget tree and understand
|
||||
which UI this code implements. This is named imperative programming style,
|
||||
|
@ -87,51 +88,42 @@ Declarative style with KV language
|
|||
from kivymd.app import MDApp
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
return Builder.load_string(
|
||||
'''
|
||||
MDScreen:
|
||||
|
||||
MDBottomNavigation:
|
||||
panel_color: "#eeeaea"
|
||||
selected_color_background: "#97ecf8"
|
||||
text_color_active: "white"
|
||||
MDNavigationBar:
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: "screen 1"
|
||||
text: "Mail"
|
||||
icon: "gmail"
|
||||
MDNavigationItem:
|
||||
|
||||
MDLabel:
|
||||
MDNavigationItemIcon:
|
||||
icon: "gmail"
|
||||
|
||||
MDNavigationItemLabel:
|
||||
text: "Mail"
|
||||
halign: "center"
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: "screen 2"
|
||||
text: "Discord"
|
||||
icon: "discord"
|
||||
MDNavigationItem:
|
||||
|
||||
MDLabel:
|
||||
text: "Discord"
|
||||
halign: "center"
|
||||
MDNavigationItemIcon:
|
||||
icon: "git"
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: "screen 3"
|
||||
text: "LinkedIN"
|
||||
icon: "linkedin"
|
||||
MDNavigationItemLabel:
|
||||
text: "GitHub"
|
||||
|
||||
MDLabel:
|
||||
MDNavigationItem:
|
||||
|
||||
MDNavigationItemIcon:
|
||||
icon: "linkedin"
|
||||
|
||||
MDNavigationItemLabel:
|
||||
text: "LinkedIN"
|
||||
halign: "center"
|
||||
'''
|
||||
)
|
||||
|
||||
|
||||
Test().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-styles-programming.png
|
||||
:align: center
|
||||
Example().run()
|
||||
|
||||
Looking at this code, we can now clearly see the widget tree and their properties.
|
||||
We can quickly navigate through the components of the screen and quickly
|
||||
|
@ -146,48 +138,41 @@ Declarative style with Python code
|
|||
.. code-block:: python
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
|
||||
from kivymd.uix.label import MDLabel
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.uix.navigationbar import (
|
||||
MDNavigationBar,
|
||||
MDNavigationItemIcon,
|
||||
MDNavigationItem,
|
||||
MDNavigationItemLabel,
|
||||
)
|
||||
|
||||
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
return (
|
||||
MDScreen(
|
||||
MDBottomNavigation(
|
||||
MDBottomNavigationItem(
|
||||
MDLabel(
|
||||
text="Mail",
|
||||
halign="center",
|
||||
),
|
||||
name="screen 1",
|
||||
text="Mail",
|
||||
icon="gmail",
|
||||
),
|
||||
MDBottomNavigationItem(
|
||||
MDLabel(
|
||||
text="Discord",
|
||||
halign="center",
|
||||
),
|
||||
name="screen 2",
|
||||
text="Discord",
|
||||
icon="discord",
|
||||
),
|
||||
MDBottomNavigationItem(
|
||||
MDLabel(
|
||||
text="LinkedIN",
|
||||
halign="center",
|
||||
),
|
||||
name="screen 3",
|
||||
text="LinkedIN",
|
||||
icon="linkedin",
|
||||
),
|
||||
panel_color="#eeeaea",
|
||||
selected_color_background="#97ecf8",
|
||||
text_color_active="white",
|
||||
)
|
||||
)
|
||||
return MDNavigationBar(
|
||||
MDNavigationItem(
|
||||
MDNavigationItemIcon(
|
||||
icon="gmail",
|
||||
),
|
||||
MDNavigationItemLabel(
|
||||
text="Mail",
|
||||
),
|
||||
),
|
||||
MDNavigationItem(
|
||||
MDNavigationItemIcon(
|
||||
icon="twitter",
|
||||
),
|
||||
MDNavigationItemLabel(
|
||||
text="Twitter",
|
||||
),
|
||||
),
|
||||
MDNavigationItem(
|
||||
MDNavigationItemIcon(
|
||||
icon="linkedin",
|
||||
),
|
||||
MDNavigationItemLabel(
|
||||
text="LinkedIN",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -239,7 +224,7 @@ get to the desired id:
|
|||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.button import MDRaisedButton
|
||||
from kivymd.uix.button import MDButton, MDButtonText
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
|
||||
|
||||
|
@ -248,18 +233,22 @@ get to the desired id:
|
|||
return (
|
||||
MDBoxLayout(
|
||||
MDFloatLayout(
|
||||
MDRaisedButton(
|
||||
MDButton(
|
||||
MDButtonText(
|
||||
text="Button 1",
|
||||
),
|
||||
id="button_1",
|
||||
text="Button 1",
|
||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
||||
),
|
||||
id="box_container_1",
|
||||
),
|
||||
MDBoxLayout(
|
||||
MDFloatLayout(
|
||||
MDRaisedButton(
|
||||
MDButton(
|
||||
MDButtonText(
|
||||
text="Button 2",
|
||||
),
|
||||
id="button_2",
|
||||
text="Button 2",
|
||||
pos_hint={"center_x": 0.5, "center_y": 0.5},
|
||||
),
|
||||
id="float_container",
|
||||
|
@ -271,13 +260,13 @@ get to the desired id:
|
|||
|
||||
def on_start(self):
|
||||
# {
|
||||
# 'box_container_1': <kivymd.uix.floatlayout.MDFloatLayout>,
|
||||
# 'box_container_2': <kivymd.uix.boxlayout.MDBoxLayout object>
|
||||
# 'button_1': <kivymd.uix.button.button.MDButton object at 0x11d93c9e0>,
|
||||
# 'button_2': <kivymd.uix.button.button.MDButton object at 0x11da128f0>,
|
||||
# 'float_container': <kivymd.uix.floatlayout.MDFloatLayout object at 0x11da228f0>,
|
||||
# 'box_container_1': <kivymd.uix.floatlayout.MDFloatLayout object at 0x11d9fc3c0>,
|
||||
# 'box_container_2': <kivymd.uix.boxlayout.MDBoxLayout object at 0x11dbf06d0>,
|
||||
# }
|
||||
print(self.root.ids)
|
||||
|
||||
# <kivymd.uix.button.button.MDRaisedButton>
|
||||
print(self.root.ids.box_container_2.ids.float_container.ids.button_2)
|
||||
print(self.root.get_ids())
|
||||
|
||||
|
||||
Example().run()
|
||||
|
@ -293,6 +282,15 @@ from kivy.properties import StringProperty
|
|||
from kivy.uix.widget import Widget
|
||||
|
||||
|
||||
class _Dict(dict):
|
||||
"""Implements access to dictionary values via a dot."""
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self[name]
|
||||
|
||||
|
||||
# TODO: Add cleaning of the `__ids` collection when removing child widgets
|
||||
# from the parent.
|
||||
class DeclarativeBehavior:
|
||||
"""
|
||||
Implements the creation and addition of child widgets as declarative
|
||||
|
@ -307,6 +305,8 @@ class DeclarativeBehavior:
|
|||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
__ids = _Dict()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
@ -314,4 +314,12 @@ class DeclarativeBehavior:
|
|||
if issubclass(child.__class__, Widget):
|
||||
self.add_widget(child)
|
||||
if hasattr(child, "id") and child.id:
|
||||
self.ids[child.id] = child
|
||||
self.__ids[child.id] = child
|
||||
|
||||
def get_ids(self) -> dict:
|
||||
"""
|
||||
Returns a dictionary of widget IDs defined in Python
|
||||
code that is written in a declarative style.
|
||||
"""
|
||||
|
||||
return self.__ids
|
||||
|
|
|
@ -31,7 +31,7 @@ For example, let's create a button with a rectangular elevation effect:
|
|||
)
|
||||
|
||||
KV = '''
|
||||
<RectangularElevationButton>
|
||||
<ElevationWidget>
|
||||
size_hint: None, None
|
||||
size: "250dp", "50dp"
|
||||
|
||||
|
@ -39,19 +39,19 @@ For example, let's create a button with a rectangular elevation effect:
|
|||
MDScreen:
|
||||
|
||||
# With elevation effect
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
pos_hint: {"center_x": .5, "center_y": .6}
|
||||
elevation: 4
|
||||
shadow_offset: 0, -6
|
||||
shadow_softness: 4
|
||||
|
||||
# Without elevation effect
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
pos_hint: {"center_x": .5, "center_y": .4}
|
||||
'''
|
||||
|
||||
|
||||
class RectangularElevationButton(
|
||||
class ElevationWidget(
|
||||
RectangularRippleBehavior,
|
||||
CommonElevationBehavior,
|
||||
ButtonBehavior,
|
||||
|
@ -84,7 +84,7 @@ For example, let's create a button with a rectangular elevation effect:
|
|||
from kivymd.uix.screen import MDScreen
|
||||
|
||||
|
||||
class RectangularElevationButton(
|
||||
class ElevationWidget(
|
||||
RectangularRippleBehavior,
|
||||
CommonElevationBehavior,
|
||||
ButtonBehavior,
|
||||
|
@ -101,13 +101,13 @@ For example, let's create a button with a rectangular elevation effect:
|
|||
def build(self):
|
||||
return (
|
||||
MDScreen(
|
||||
RectangularElevationButton(
|
||||
ElevationWidget(
|
||||
pos_hint={"center_x": .5, "center_y": .6},
|
||||
elevation=4,
|
||||
shadow_softness=4,
|
||||
shadow_offset=(0, -6),
|
||||
),
|
||||
RectangularElevationButton(
|
||||
ElevationWidget(
|
||||
pos_hint={"center_x": .5, "center_y": .4},
|
||||
),
|
||||
)
|
||||
|
@ -271,7 +271,7 @@ Animating the elevation
|
|||
size: 100, 100
|
||||
md_bg_color: 0, 0, 1, 1
|
||||
elevation: 2
|
||||
radius: 18
|
||||
radius: dp(18)
|
||||
'''
|
||||
|
||||
|
||||
|
@ -306,6 +306,7 @@ Animating the elevation
|
|||
|
||||
from kivy.animation import Animation
|
||||
from kivy.uix.behaviors import ButtonBehavior
|
||||
from kivy.metrics import dp
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.behaviors import CommonElevationBehavior, RectangularRippleBehavior
|
||||
|
@ -341,7 +342,7 @@ Animating the elevation
|
|||
size=(100, 100),
|
||||
md_bg_color="blue",
|
||||
elevation=2,
|
||||
radius=18,
|
||||
radius=dp(18),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -355,23 +356,17 @@ Animating the elevation
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = (
|
||||
"CommonElevationBehavior",
|
||||
"RectangularElevationBehavior",
|
||||
"CircularElevationBehavior",
|
||||
"RoundedRectangularElevationBehavior",
|
||||
"FakeRectangularElevationBehavior",
|
||||
"FakeCircularElevationBehavior",
|
||||
)
|
||||
__all__ = ("CommonElevationBehavior",)
|
||||
|
||||
from kivy import Logger
|
||||
from kivy.lang import Builder
|
||||
from kivy.metrics import dp
|
||||
from kivy.properties import (
|
||||
BoundedNumericProperty,
|
||||
ColorProperty,
|
||||
ListProperty,
|
||||
NumericProperty,
|
||||
VariableListProperty,
|
||||
DictProperty,
|
||||
)
|
||||
from kivy.uix.widget import Widget
|
||||
|
||||
|
@ -393,18 +388,15 @@ Builder.load_string(
|
|||
axis: tuple(self.rotate_value_axis)
|
||||
origin: self.center
|
||||
Color:
|
||||
rgba:
|
||||
(0, 0, 0, 0) \
|
||||
if self.disabled or not self.elevation else \
|
||||
root.shadow_color
|
||||
rgba: root.shadow_color
|
||||
BoxShadow:
|
||||
pos: self.pos
|
||||
pos: self.pos if not isinstance(self, RelativeLayout) else (0, 0)
|
||||
size: self.size
|
||||
offset: root.shadow_offset
|
||||
spread_radius: -(root.shadow_softness), -(root.shadow_softness)
|
||||
blur_radius: root.elevation * 10
|
||||
blur_radius: root.elevation_levels[root.elevation_level]
|
||||
border_radius:
|
||||
(root.radius if hasattr(self, "radius") else [0, 0, 0, 0]) \
|
||||
(root.radius if hasattr(self, "radius") and root.radius else [0, 0, 0, 0]) \
|
||||
if root.shadow_radius == [0.0, 0.0, 0.0, 0.0] else \
|
||||
root.shadow_radius
|
||||
canvas.after:
|
||||
|
@ -421,6 +413,36 @@ class CommonElevationBehavior(Widget):
|
|||
class documentation.
|
||||
"""
|
||||
|
||||
elevation_level = BoundedNumericProperty(0, min=0, max=5)
|
||||
"""
|
||||
Elevation level (values from 0 to 5)
|
||||
|
||||
.. versionadded:: 1.2.0
|
||||
|
||||
:attr:`elevation_level` is an :class:`~kivy.properties.BoundedNumericProperty`
|
||||
and defaults to `0`.
|
||||
"""
|
||||
|
||||
elevation_levels = DictProperty(
|
||||
{
|
||||
0: 0,
|
||||
1: dp(8),
|
||||
2: dp(12),
|
||||
3: dp(16),
|
||||
4: dp(20),
|
||||
5: dp(24),
|
||||
}
|
||||
)
|
||||
"""
|
||||
Elevation is measured as the distance between components along the z-axis
|
||||
in density-independent pixels (dps).
|
||||
|
||||
.. versionadded:: 1.2.0
|
||||
|
||||
:attr:`elevation_levels` is an :class:`~kivy.properties.DictProperty`
|
||||
and defaults to `{0: dp(0), 1: dp(8), 2: dp(23), 3: dp(16), 4: dp(20), 5: dp(24)}`.
|
||||
"""
|
||||
|
||||
elevation = BoundedNumericProperty(0, min=0, errorvalue=0)
|
||||
"""
|
||||
Elevation of the widget.
|
||||
|
@ -449,7 +471,7 @@ class CommonElevationBehavior(Widget):
|
|||
MDScreen:
|
||||
|
||||
MDCard:
|
||||
radius: 12, 46, 12, 46
|
||||
radius: dp(12), dp(46), dp(12), dp(46)
|
||||
size_hint: .5, .3
|
||||
pos_hint: {"center_x": .5, "center_y": .5}
|
||||
elevation: 2
|
||||
|
@ -486,26 +508,26 @@ class CommonElevationBehavior(Widget):
|
|||
from kivymd.uix.behaviors import BackgroundColorBehavior, CommonElevationBehavior
|
||||
|
||||
KV = '''
|
||||
<RectangularElevationButton>
|
||||
<ElevationWidget>
|
||||
size_hint: None, None
|
||||
size: "250dp", "50dp"
|
||||
|
||||
|
||||
MDScreen:
|
||||
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
pos_hint: {"center_x": .5, "center_y": .6}
|
||||
elevation: 6
|
||||
shadow_softness: 6
|
||||
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
pos_hint: {"center_x": .5, "center_y": .4}
|
||||
elevation: 6
|
||||
shadow_softness: 12
|
||||
'''
|
||||
|
||||
|
||||
class RectangularElevationButton(CommonElevationBehavior, BackgroundColorBehavior):
|
||||
class ElevationWidget(CommonElevationBehavior, BackgroundColorBehavior):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.md_bg_color = "blue"
|
||||
|
@ -522,19 +544,7 @@ class CommonElevationBehavior(Widget):
|
|||
:align: center
|
||||
|
||||
:attr:`shadow_softness` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `12`.
|
||||
"""
|
||||
|
||||
shadow_softness_size = BoundedNumericProperty(2, min=2, deprecated=True)
|
||||
"""
|
||||
The value of the softness of the shadow.
|
||||
|
||||
.. versionadded:: 1.1.0
|
||||
|
||||
.. deprecated:: 1.2.0
|
||||
|
||||
:attr:`shadow_softness_size` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `2`.
|
||||
and defaults to `0.0`.
|
||||
"""
|
||||
|
||||
shadow_offset = ListProperty((0, 0))
|
||||
|
@ -551,23 +561,23 @@ class CommonElevationBehavior(Widget):
|
|||
from kivymd.uix.behaviors import BackgroundColorBehavior, CommonElevationBehavior
|
||||
|
||||
KV = '''
|
||||
<RectangularElevationButton>
|
||||
<ElevationWidget>
|
||||
size_hint: None, None
|
||||
size: "100dp", "100dp"
|
||||
|
||||
|
||||
MDScreen:
|
||||
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
pos_hint: {"center_x": .5, "center_y": .5}
|
||||
elevation: 6
|
||||
shadow_radius: 6
|
||||
shadow_radius: dp(6)
|
||||
shadow_softness: 12
|
||||
shadow_offset: -12, -12
|
||||
'''
|
||||
|
||||
|
||||
class RectangularElevationButton(CommonElevationBehavior, BackgroundColorBehavior):
|
||||
class ElevationWidget(CommonElevationBehavior, BackgroundColorBehavior):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.md_bg_color = "blue"
|
||||
|
@ -585,7 +595,7 @@ class CommonElevationBehavior(Widget):
|
|||
|
||||
.. code-block:: kv
|
||||
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
shadow_offset: 12, -12
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-2.png
|
||||
|
@ -593,7 +603,7 @@ class CommonElevationBehavior(Widget):
|
|||
|
||||
.. code-block:: kv
|
||||
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
shadow_offset: 12, 12
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-3.png
|
||||
|
@ -601,7 +611,7 @@ class CommonElevationBehavior(Widget):
|
|||
|
||||
.. code-block:: kv
|
||||
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
shadow_offset: -12, 12
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-offset-4.png
|
||||
|
@ -619,7 +629,7 @@ class CommonElevationBehavior(Widget):
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
RectangularElevationButton:
|
||||
ElevationWidget:
|
||||
shadow_color: 0, 0, 1, .8
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/shadow-color.png
|
||||
|
@ -691,82 +701,10 @@ class CommonElevationBehavior(Widget):
|
|||
and defaults to `(0, 0, 1)`.
|
||||
"""
|
||||
|
||||
_elevation = 0
|
||||
# _elevation = 0
|
||||
_elevation_level = 0
|
||||
_shadow_softness = 0
|
||||
_shadow_color = (0, 0, 0, 0)
|
||||
|
||||
def on_elevation(self, instance, value) -> None:
|
||||
self._elevation = value
|
||||
|
||||
|
||||
class RectangularElevationBehavior(CommonElevationBehavior):
|
||||
"""
|
||||
.. deprecated:: 1.1.0
|
||||
Use :class:`~CommonElevationBehavior` class instead.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
Logger.warning(
|
||||
"KivyMD: "
|
||||
"The `RectangularElevationBehavior` class has been deprecated. "
|
||||
"Use the `CommonElevationBehavior` class instead.`"
|
||||
)
|
||||
|
||||
|
||||
class CircularElevationBehavior(CommonElevationBehavior):
|
||||
"""
|
||||
.. deprecated:: 1.1.0
|
||||
Use :class:`~CommonElevationBehavior` class instead.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
Logger.warning(
|
||||
"KivyMD: "
|
||||
"The `CircularElevationBehavior` class has been deprecated. "
|
||||
"Use the `CommonElevationBehavior` class instead.`"
|
||||
)
|
||||
|
||||
|
||||
class RoundedRectangularElevationBehavior(CommonElevationBehavior):
|
||||
"""
|
||||
.. deprecated:: 1.1.0
|
||||
Use :class:`~CommonElevationBehavior` class instead.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
Logger.warning(
|
||||
"KivyMD: "
|
||||
"The `RoundedRectangularElevationBehavior` class has been "
|
||||
"deprecated. Use the `CommonElevationBehavior` class instead.`"
|
||||
)
|
||||
|
||||
|
||||
class FakeRectangularElevationBehavior(CommonElevationBehavior):
|
||||
"""
|
||||
.. deprecated:: 1.1.0
|
||||
Use :class:`~CommonElevationBehavior` class instead.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
Logger.warning(
|
||||
"KivyMD: "
|
||||
"The `FakeRectangularElevationBehavior` class has been "
|
||||
"deprecated. Use the `CommonElevationBehavior` class instead."
|
||||
)
|
||||
|
||||
|
||||
class FakeCircularElevationBehavior(CommonElevationBehavior):
|
||||
"""
|
||||
.. deprecated:: 1.1.0
|
||||
Use :class:`~CommonElevationBehavior` class instead.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
Logger.warning(
|
||||
"KivyMD: "
|
||||
"The `FakeCircularElevationBehavior` class has been deprecated. "
|
||||
"Use the `CommonElevationBehavior` class instead."
|
||||
)
|
||||
# def on_elevation(self, instance, value) -> None:
|
||||
# self._elevation = value
|
||||
|
|
|
@ -15,7 +15,7 @@ Usage
|
|||
from kivy.lang import Builder
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.behaviors import RectangularElevationBehavior
|
||||
from kivymd.uix.behaviors import CommonElevationBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.behaviors.focus_behavior import FocusBehavior
|
||||
|
||||
|
@ -36,7 +36,7 @@ Usage
|
|||
'''
|
||||
|
||||
|
||||
class FocusWidget(MDBoxLayout, RectangularElevationBehavior, FocusBehavior):
|
||||
class FocusWidget(MDBoxLayout, CommonElevationBehavior, FocusBehavior):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -65,25 +65,26 @@ Color change at focus/defocus
|
|||
|
||||
__all__ = ("FocusBehavior",)
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.properties import BooleanProperty, ColorProperty
|
||||
from kivy.uix.behaviors import ButtonBehavior
|
||||
|
||||
from kivymd.uix.behaviors import HoverBehavior
|
||||
|
||||
|
||||
class FocusBehavior(HoverBehavior, ButtonBehavior):
|
||||
class FocusBehavior(HoverBehavior):
|
||||
"""
|
||||
Focus behavior class.
|
||||
|
||||
For more information, see in the :class:`~kivymd.uix.behavior.HoverBehavior`
|
||||
and :class:`~kivy.uix.button.ButtonBehavior` classes documentation.
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.behavior.HoverBehavior` and
|
||||
:class:`~kivy.uix.button.ButtonBehavior`
|
||||
classes documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_enter`
|
||||
Called when mouse enters the bbox of the widget AND the widget is visible
|
||||
Fired when mouse enters the bbox of the widget AND the widget is
|
||||
visible.
|
||||
:attr:`on_leave`
|
||||
Called when the mouse exits the widget AND the widget is visible
|
||||
Fired when the mouse exits the widget AND the widget is visible.
|
||||
"""
|
||||
|
||||
focus_behavior = BooleanProperty(True)
|
||||
|
@ -109,39 +110,3 @@ class FocusBehavior(HoverBehavior, ButtonBehavior):
|
|||
:attr:`unfocus_color` is a :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
def on_enter(self):
|
||||
"""Called when mouse enter the bbox of the widget."""
|
||||
|
||||
if (
|
||||
hasattr(self, "md_bg_color") or hasattr(self, "bg_color")
|
||||
) and self.focus_behavior:
|
||||
if hasattr(self, "theme_cls") and not self.focus_color:
|
||||
color = self.theme_cls.bg_normal
|
||||
else:
|
||||
if not self.focus_color:
|
||||
color = App.get_running_app().theme_cls.bg_normal
|
||||
else:
|
||||
color = self.focus_color
|
||||
self._set_bg_color(color)
|
||||
|
||||
def on_leave(self):
|
||||
"""Called when the mouse exit the widget."""
|
||||
|
||||
if (
|
||||
hasattr(self, "md_bg_color") or hasattr(self, "bg_color")
|
||||
) and self.focus_behavior:
|
||||
if hasattr(self, "theme_cls") and not self.unfocus_color:
|
||||
color = self.theme_cls.bg_light
|
||||
else:
|
||||
if not self.unfocus_color:
|
||||
color = App.get_running_app().theme_cls.bg_light
|
||||
else:
|
||||
color = self.unfocus_color
|
||||
self._set_bg_color(color)
|
||||
|
||||
def _set_bg_color(self, color):
|
||||
if hasattr(self, "md_bg_color"):
|
||||
self.md_bg_color = color
|
||||
elif hasattr(self, "bg_color"):
|
||||
self.bg_color = color
|
||||
|
|
|
@ -27,9 +27,13 @@ the widget.
|
|||
|
||||
.. note::
|
||||
|
||||
:class:`~HoverBehavior` will by default check to see if the current Widget is visible (i.e. not covered by a modal or popup and not a part of a Relative Layout, MDTab or Carousel that is not currently visible etc) and will only issue events if the widget is visible.
|
||||
:class:`~HoverBehavior` will by default check to see if the current Widget
|
||||
is visible (i.e. not covered by a modal or popup and not a part of a
|
||||
RelativeLayout, MDTab or Carousel that is not currently visible etc)
|
||||
and will only issue events if the widget is visible.
|
||||
|
||||
To get the legacy behavior that the events are always triggered, you can set `detect_visible` on the Widget to `False`.
|
||||
To get the legacy behavior that the events are always triggered, you can
|
||||
set `detect_visible` on the Widget to `False`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -40,13 +44,14 @@ the widget.
|
|||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
|
||||
KV = '''
|
||||
Screen
|
||||
MDScreen
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
|
||||
MDBoxLayout:
|
||||
id: box
|
||||
pos_hint: {'center_x': .5, 'center_y': .5}
|
||||
size_hint: .8, .8
|
||||
md_bg_color: app.theme_cls.bg_darkest
|
||||
md_bg_color: self.theme_cls.secondaryContainerColor
|
||||
'''
|
||||
|
||||
|
||||
|
@ -54,19 +59,23 @@ the widget.
|
|||
'''Custom item implementing hover behavior.'''
|
||||
|
||||
def on_enter(self, *args):
|
||||
'''The method will be called when the mouse cursor
|
||||
is within the borders of the current widget.'''
|
||||
'''
|
||||
The method will be called when the mouse cursor
|
||||
is within the borders of the current widget.
|
||||
'''
|
||||
|
||||
self.md_bg_color = (1, 1, 1, 1)
|
||||
self.md_bg_color = "white"
|
||||
|
||||
def on_leave(self, *args):
|
||||
'''The method will be called when the mouse cursor goes beyond
|
||||
the borders of the current widget.'''
|
||||
'''
|
||||
The method will be called when the mouse cursor goes beyond
|
||||
the borders of the current widget.
|
||||
'''
|
||||
|
||||
self.md_bg_color = self.theme_cls.bg_darkest
|
||||
self.md_bg_color = self.theme_cls.secondaryContainerColor
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
self.screen = Builder.load_string(KV)
|
||||
for i in range(5):
|
||||
|
@ -74,10 +83,9 @@ the widget.
|
|||
return self.screen
|
||||
|
||||
|
||||
Test().run()
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/hover-behavior.gif
|
||||
:width: 250 px
|
||||
:align: center
|
||||
"""
|
||||
|
||||
|
@ -85,23 +93,25 @@ __all__ = ("HoverBehavior",)
|
|||
|
||||
from kivy.core.window import Window
|
||||
from kivy.properties import BooleanProperty, ObjectProperty
|
||||
from kivy.uix.relativelayout import RelativeLayout
|
||||
from kivy.uix.widget import Widget
|
||||
|
||||
|
||||
class HoverBehavior(object):
|
||||
class HoverBehavior:
|
||||
"""
|
||||
:Events:
|
||||
:attr:`on_enter`
|
||||
Called when mouse enters the bbox of the widget AND the widget is visible
|
||||
Fired when mouse enters the bbox of the widget and the widget is
|
||||
visible.
|
||||
:attr:`on_leave`
|
||||
Called when the mouse exits the widget AND the widget is visible
|
||||
Fired when the mouse exits the widget and the widget is visible.
|
||||
"""
|
||||
|
||||
hovering = BooleanProperty(False)
|
||||
"""
|
||||
`True`, if the mouse cursor is within the borders of the widget.
|
||||
|
||||
Note that this is set and cleared even if the widget is not visible
|
||||
Note that this is set and cleared even if the widget is not visible.
|
||||
|
||||
:attr:`hover` is a :class:`~kivy.properties.BooleanProperty`
|
||||
and defaults to `False`.
|
||||
|
@ -109,7 +119,7 @@ class HoverBehavior(object):
|
|||
|
||||
hover_visible = BooleanProperty(False)
|
||||
"""
|
||||
`True` if hovering is True AND is the current widget is visible
|
||||
`True` if hovering is `True` and is the current widget is visible.
|
||||
|
||||
:attr:`hover_visible` is a :class:`~kivy.properties.BooleanProperty`
|
||||
and defaults to `False`.
|
||||
|
@ -118,7 +128,7 @@ class HoverBehavior(object):
|
|||
enter_point = ObjectProperty(allownone=True)
|
||||
"""
|
||||
Holds the last position where the mouse pointer crossed into the Widget
|
||||
if the Widget is visible and is currently in a hovering state
|
||||
if the Widget is visible and is currently in a hovering state.
|
||||
|
||||
:attr:`enter_point` is a :class:`~kivy.properties.ObjectProperty`
|
||||
and defaults to `None`.
|
||||
|
@ -132,22 +142,24 @@ class HoverBehavior(object):
|
|||
and defaults to `True`.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.register_event_type("on_enter")
|
||||
self.register_event_type("on_leave")
|
||||
Window.bind(mouse_pos=self.on_mouse_update)
|
||||
super(HoverBehavior, self).__init__(**kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def on_mouse_update(self, *args):
|
||||
# If the Widget currently has no parent, do nothing
|
||||
# If the Widget currently has no parent, do nothing.
|
||||
if not self.get_root_window():
|
||||
return
|
||||
pos = args[1]
|
||||
#
|
||||
# is the pointer in the same position as the widget?
|
||||
# If not - then issue an on_exit event if needed
|
||||
#
|
||||
if not self.collide_point(*self.to_widget(*pos)):
|
||||
# Is the pointer in the same position as the widget?
|
||||
# If not - then issue an on_exit event if needed.
|
||||
if not self.collide_point(
|
||||
*self.to_widget(*pos)
|
||||
if not isinstance(self, RelativeLayout)
|
||||
else (pos[0], pos[1])
|
||||
):
|
||||
self.hovering = False
|
||||
self.enter_point = None
|
||||
if self.hover_visible:
|
||||
|
@ -155,72 +167,59 @@ class HoverBehavior(object):
|
|||
self.dispatch("on_leave")
|
||||
return
|
||||
|
||||
#
|
||||
# The pointer is in the same position as the widget
|
||||
#
|
||||
|
||||
# The pointer is in the same position as the widget.
|
||||
if self.hovering:
|
||||
#
|
||||
# nothing to do here. Not - this does not handle the case where
|
||||
# a popup comes over an existing hover event.
|
||||
# This seems reasonable
|
||||
#
|
||||
# Nothing to do here. Not - this does not handle the case where
|
||||
# a popup comes over an existing hover event.
|
||||
# This seems reasonable.
|
||||
return
|
||||
|
||||
#
|
||||
# Otherwise - set the hovering attribute
|
||||
#
|
||||
self.hovering = True
|
||||
|
||||
#
|
||||
# We need to traverse the tree to see if the Widget is visible
|
||||
#
|
||||
# This is a two stage process:
|
||||
# - first go up the tree to the root Window.
|
||||
# At each stage - check that the Widget is actually visible
|
||||
# - Second - At the root Window check that there is not another branch
|
||||
# covering the Widget
|
||||
#
|
||||
|
||||
# We need to traverse the tree to see if the Widget is visible.
|
||||
# This is a two stage process - first go up the tree to the root.
|
||||
# Window. At each stage - check that the Widget is actually visible.
|
||||
# Second - at the root Window check that there is not another branch
|
||||
# covering the Widget.
|
||||
self.hover_visible = True
|
||||
|
||||
if self.detect_visible:
|
||||
widget: Widget = self
|
||||
while True:
|
||||
# Walk up the Widget tree from the target Widget
|
||||
# Walk up the Widget tree from the target Widget.
|
||||
parent = widget.parent
|
||||
try:
|
||||
# See if the mouse point collides with the parent
|
||||
# using both local and glabal coordinates to cover absoluet and relative layouts
|
||||
# using both local and global coordinates to cover absolute
|
||||
# and relative layouts.
|
||||
pinside = parent.collide_point(
|
||||
*parent.to_widget(*pos)
|
||||
) or parent.collide_point(*pos)
|
||||
except Exception:
|
||||
# The collide_point will error when you reach the root Window
|
||||
# The collide_point will error when you reach the root
|
||||
# Window.
|
||||
break
|
||||
if not pinside:
|
||||
self.hover_visible = False
|
||||
break
|
||||
# Iterate upwards
|
||||
# Iterate upwards.
|
||||
widget = parent
|
||||
|
||||
#
|
||||
# parent = root window
|
||||
# widget = first Widget on the current branch
|
||||
#
|
||||
|
||||
children = parent.children
|
||||
for child in children:
|
||||
# For each top level widget - check if is current branch
|
||||
# For each top level widget - check if is current branch.
|
||||
# If it is - then break.
|
||||
# If not then - since we start at 0 - this widget is visible
|
||||
#
|
||||
# Check to see if it should take the hover
|
||||
#
|
||||
# If not then - since we start at 0 - this widget is visible.
|
||||
# Check to see if it should take the hover.
|
||||
if child == widget:
|
||||
# this means that the current widget is visible
|
||||
# This means that the current widget is visible.
|
||||
break
|
||||
if child.collide_point(*pos):
|
||||
# this means that the current widget is covered by a modal or popup
|
||||
# This means that the current widget is covered by a modal
|
||||
# or popup.
|
||||
self.hover_visible = False
|
||||
break
|
||||
if self.hover_visible:
|
||||
|
@ -228,7 +227,7 @@ class HoverBehavior(object):
|
|||
self.dispatch("on_enter")
|
||||
|
||||
def on_enter(self):
|
||||
"""Called when mouse enters the bbox of the widget AND the widget is visible."""
|
||||
"""Fired when mouse enter the bbox of the widget."""
|
||||
|
||||
def on_leave(self):
|
||||
"""Called when the mouse exits the widget AND the widget is visible."""
|
||||
"""Fired when the mouse goes outside the widget border."""
|
||||
|
|
|
@ -15,17 +15,20 @@ as dialogs, dropdown menu, snack bars, and so on.
|
|||
|
||||
__all__ = (
|
||||
"MotionBase",
|
||||
"MotionExtendedFabButtonBehavior",
|
||||
"MotionDropDownMenuBehavior",
|
||||
"MotionDialogBehavior",
|
||||
"MotionShackBehavior",
|
||||
"MotionDatePickerBehavior",
|
||||
"MotionTimePickerBehavior",
|
||||
)
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.window import Window
|
||||
from kivy.properties import StringProperty, NumericProperty
|
||||
|
||||
from kivymd.uix.behaviors.stencil_behavior import StencilBehavior
|
||||
from kivy.metrics import dp
|
||||
from kivy.properties import StringProperty, NumericProperty, ObjectProperty
|
||||
from kivymd.uix.behaviors.scale_behavior import ScaleBehavior
|
||||
|
||||
|
||||
class MotionBase:
|
||||
|
@ -166,80 +169,288 @@ class MotionDropDownMenuBehavior(MotionBase):
|
|||
self.scale_value_y = value
|
||||
|
||||
|
||||
class MotionDialogBehavior(MotionBase):
|
||||
class MotionExtendedFabButtonBehavior(MotionBase):
|
||||
"""
|
||||
Base class for dialog movement behavior.
|
||||
Base class for extended Fab button movement behavior.
|
||||
|
||||
For more information, see in the :class:`~MotionBase` class documentation.
|
||||
"""
|
||||
|
||||
show_duration = NumericProperty(0.1)
|
||||
show_transition = StringProperty("out_circ")
|
||||
"""
|
||||
The type of transition of the widget opening.
|
||||
|
||||
:attr:`show_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'out_circ'`.
|
||||
"""
|
||||
|
||||
shift_transition = StringProperty("out_sine")
|
||||
"""
|
||||
Text label transition.
|
||||
|
||||
:attr:`shift_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'out_sine'`.
|
||||
"""
|
||||
|
||||
show_duration = NumericProperty(0.3)
|
||||
"""
|
||||
Duration of widget display transition.
|
||||
|
||||
:attr:`show_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.1`.
|
||||
and defaults to `0.3`.
|
||||
"""
|
||||
|
||||
scale_x = NumericProperty(1.5)
|
||||
hide_transition = StringProperty("out_sine")
|
||||
"""
|
||||
Default X-axis scaling values.
|
||||
The type of transition of the widget closing.
|
||||
|
||||
:attr:`scale_x` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `1.5`.
|
||||
:attr:`hide_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'linear'`.
|
||||
"""
|
||||
|
||||
scale_y = NumericProperty(1.5)
|
||||
hide_duration = NumericProperty(0.2)
|
||||
"""
|
||||
Default Y-axis scaling values.
|
||||
Duration of widget closing transition.
|
||||
|
||||
:attr:`scale_y` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `1.5`.
|
||||
:attr:`hide_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.2`.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.set_default_values()
|
||||
_x = NumericProperty(0)
|
||||
_anim_opacity = None
|
||||
|
||||
def set_default_values(self):
|
||||
"""Sets default scaled and transparency values."""
|
||||
def collapse(self, *args) -> None:
|
||||
"""Collapses the button."""
|
||||
|
||||
self.scale_value_x = self.scale_x
|
||||
self.scale_value_y = self.scale_y
|
||||
self.opacity = 0
|
||||
def collapse(*args):
|
||||
if self._label and self._icon:
|
||||
Animation(_x=0, d=self.hide_duration).start(self)
|
||||
anim = Animation(
|
||||
width=dp(56), d=self.hide_duration, t=self.hide_transition
|
||||
)
|
||||
anim.bind(on_progress=self._check_collapse_progress)
|
||||
anim.start(self)
|
||||
|
||||
Clock.schedule_once(collapse)
|
||||
|
||||
def expand(self, *args) -> None:
|
||||
"""Expands the button."""
|
||||
|
||||
def expand(*args):
|
||||
if self._label and self._icon:
|
||||
anim = Animation(
|
||||
width=self.width
|
||||
+ self._label.texture_size[0]
|
||||
+ (dp(18) if self._icon else 0),
|
||||
d=self.show_duration,
|
||||
t=self.show_transition,
|
||||
)
|
||||
anim.bind(on_progress=self._check_expand_progress)
|
||||
anim.start(self)
|
||||
Animation(
|
||||
_x=dp(12), d=self.show_duration, t=self.shift_transition
|
||||
).start(self)
|
||||
|
||||
Clock.schedule_once(expand)
|
||||
|
||||
def set_opacity_text_button(self, value: int) -> None:
|
||||
if self._label:
|
||||
self._anim_opacity = Animation(
|
||||
opacity=value,
|
||||
d=self.show_duration * 16.666666666666668 / 100
|
||||
if value
|
||||
else self.show_duration * 1.6666666666666667 / 100,
|
||||
)
|
||||
self._anim_opacity.bind(
|
||||
on_complete=lambda *x: setattr(self, "_anim_opacity", None)
|
||||
)
|
||||
self._anim_opacity.start(self._label)
|
||||
|
||||
def _check_collapse_progress(self, animation, instance, progress) -> None:
|
||||
if progress > 0.1:
|
||||
if not self._anim_opacity:
|
||||
self.set_opacity_text_button(0)
|
||||
|
||||
def _check_expand_progress(self, animation, instance, progress) -> None:
|
||||
if progress > 0.3:
|
||||
if not self._anim_opacity:
|
||||
self.set_opacity_text_button(1)
|
||||
|
||||
|
||||
class MotionDialogBehavior(ScaleBehavior, MotionBase):
|
||||
"""
|
||||
Base class for dialog movement behavior.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior`
|
||||
:class:`~MotionBase`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
show_transition = StringProperty("out_expo")
|
||||
"""
|
||||
The type of transition of the widget opening.
|
||||
|
||||
:attr:`show_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'out_expo'`.
|
||||
"""
|
||||
|
||||
show_button_container_transition = StringProperty("out_circ")
|
||||
"""
|
||||
The type of transition of the widget opening.
|
||||
|
||||
:attr:`show_button_container_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'out_circ'`.
|
||||
"""
|
||||
|
||||
hide_transition = StringProperty("out_circ")
|
||||
"""
|
||||
The type of transition of the widget opening.
|
||||
|
||||
:attr:`show_transition` is a :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'hide_transition'`.
|
||||
"""
|
||||
|
||||
show_duration = NumericProperty(0.4)
|
||||
"""
|
||||
Duration of widget display transition.
|
||||
|
||||
:attr:`show_duration` is a :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.2`.
|
||||
"""
|
||||
|
||||
def on_dismiss(self, *args):
|
||||
"""Called when a dialog closed."""
|
||||
"""Fired when a dialog closed."""
|
||||
|
||||
self.set_default_values()
|
||||
def remove_dialog(*args):
|
||||
Window.remove_widget(self)
|
||||
if self._scrim:
|
||||
Window.remove_widget(self._scrim)
|
||||
|
||||
def on_open(self, *args):
|
||||
"""Called when a dialog opened."""
|
||||
if self._scrim:
|
||||
Animation(alpha=0, d=self.hide_duration).start(self._scrim)
|
||||
|
||||
Animation(
|
||||
opacity=1,
|
||||
scale_value_x=1,
|
||||
scale_value_y=1,
|
||||
t=self.show_transition,
|
||||
d=self.show_duration,
|
||||
).start(self)
|
||||
y=self.ids.content_container.y,
|
||||
t=self.hide_transition,
|
||||
d=self.hide_duration,
|
||||
).start(self.ids.button_container)
|
||||
|
||||
anim = Animation(
|
||||
opacity=0,
|
||||
scale_value_y=0,
|
||||
t=self.hide_transition,
|
||||
d=self.hide_duration,
|
||||
)
|
||||
anim.bind(on_complete=remove_dialog)
|
||||
anim.start(self)
|
||||
|
||||
def on_open(self, *args):
|
||||
"""Fired when a dialog opened."""
|
||||
|
||||
def open(*args):
|
||||
self.scale_value_y = 0
|
||||
self.scale_value_center = (0, self.center[1] + self.height / 2)
|
||||
Animation(
|
||||
opacity=1,
|
||||
scale_value_y=1,
|
||||
t=self.show_transition,
|
||||
d=self.show_duration,
|
||||
).start(self)
|
||||
|
||||
Animation(
|
||||
y=dp(24),
|
||||
t=self.show_button_container_transition,
|
||||
d=self.show_duration + 0.15,
|
||||
).start(self.ids.button_container)
|
||||
|
||||
if self._scrim:
|
||||
Animation(alpha=0.4, d=self.show_duration).start(self._scrim)
|
||||
|
||||
Clock.schedule_once(open)
|
||||
|
||||
|
||||
class MotionShackBehavior(StencilBehavior, MotionBase):
|
||||
class MotionPickerBehavior(MotionDialogBehavior):
|
||||
"""
|
||||
Base class for date/time pickers movement behavior.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~MotionDialogBehavior` class documentation.
|
||||
"""
|
||||
|
||||
_scrim = ObjectProperty()
|
||||
|
||||
def on_open(self, *args):
|
||||
"""Fired when a dialog opened."""
|
||||
|
||||
def open(*args):
|
||||
self.scale_value_y = 0
|
||||
self.scale_value_center = (0, self.center[1] + self.height / 2)
|
||||
Animation(
|
||||
opacity=1,
|
||||
scale_value_y=1,
|
||||
t=self.show_transition,
|
||||
d=self.show_duration,
|
||||
).start(self)
|
||||
|
||||
if self._scrim:
|
||||
Animation(alpha=0.4, d=self.show_duration).start(self._scrim)
|
||||
|
||||
Clock.schedule_once(open)
|
||||
|
||||
def on_dismiss(self, *args):
|
||||
"""Fired when a dialog closed."""
|
||||
|
||||
def remove_dialog(*args):
|
||||
Window.remove_widget(self)
|
||||
if self._scrim:
|
||||
Window.remove_widget(self._scrim)
|
||||
self.dispatch("on_dismiss")
|
||||
|
||||
if self._scrim:
|
||||
Animation(alpha=0, d=self.hide_duration).start(self._scrim)
|
||||
|
||||
anim = Animation(
|
||||
opacity=0,
|
||||
scale_value_y=0,
|
||||
t=self.hide_transition,
|
||||
d=self.hide_duration,
|
||||
)
|
||||
anim.bind(on_complete=remove_dialog)
|
||||
anim.start(self)
|
||||
|
||||
|
||||
class MotionTimePickerBehavior(MotionPickerBehavior):
|
||||
"""
|
||||
Base class for time picker movement behavior.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~MotionPickerBehavior` class documentation.
|
||||
"""
|
||||
|
||||
|
||||
class MotionDatePickerBehavior(MotionPickerBehavior):
|
||||
"""
|
||||
Base class for date picker movement behavior.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~MotionPickerBehavior` class documentation.
|
||||
"""
|
||||
|
||||
|
||||
class MotionShackBehavior(MotionBase):
|
||||
"""
|
||||
The base class for the behavior of the movement of snack bars.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~MotionBase` class and
|
||||
:class:`~kivy.uix.behaviors.stencil_behavior.StencilBehavior` class
|
||||
documentation.
|
||||
:class:`~MotionBase` class documentation.
|
||||
"""
|
||||
|
||||
_interval = 0
|
||||
_height = 0
|
||||
|
||||
def on_dismiss(self, *args):
|
||||
"""Called when a snackbar closed."""
|
||||
"""Fired when a snackbar closed."""
|
||||
|
||||
def remove_snackbar(*args):
|
||||
Window.parent.remove_widget(self)
|
||||
|
@ -257,7 +468,7 @@ class MotionShackBehavior(StencilBehavior, MotionBase):
|
|||
anim.start(self)
|
||||
|
||||
def on_open(self, *args):
|
||||
"""Called when a snackbar opened."""
|
||||
"""Fired when a snackbar opened."""
|
||||
|
||||
def open(*args):
|
||||
self._height = self.height
|
||||
|
@ -274,9 +485,9 @@ class MotionShackBehavior(StencilBehavior, MotionBase):
|
|||
)
|
||||
)
|
||||
anim.start(self)
|
||||
self.dispatch("on_open")
|
||||
|
||||
Clock.schedule_once(open)
|
||||
self.dispatch("on_open")
|
||||
Clock.schedule_once(open, 0.2)
|
||||
|
||||
def _wait_interval(self, interval):
|
||||
self._interval += interval
|
||||
|
|
|
@ -285,11 +285,18 @@ class CommonRipple:
|
|||
and defaults to `'ripple_func_out'`.
|
||||
"""
|
||||
|
||||
ripple_effect = BooleanProperty(True)
|
||||
"""
|
||||
Should I use the ripple effect.
|
||||
|
||||
:attr:`ripple_effect` is an :class:`~kivy.properties.BooleanProperty`
|
||||
and defaults to `True`.
|
||||
"""
|
||||
|
||||
_ripple_rad = NumericProperty()
|
||||
_doing_ripple = BooleanProperty(False)
|
||||
_finishing_ripple = BooleanProperty(False)
|
||||
_fading_out = BooleanProperty(False)
|
||||
_no_ripple_effect = BooleanProperty(False)
|
||||
_round_rad = ListProperty([0, 0, 0, 0])
|
||||
|
||||
def lay_canvas_instructions(self) -> NoReturn:
|
||||
|
@ -333,6 +340,8 @@ class CommonRipple:
|
|||
anim.start(self)
|
||||
|
||||
def anim_complete(self, *args) -> None:
|
||||
"""Fired when the "fade_out" animation complete."""
|
||||
|
||||
self._doing_ripple = False
|
||||
self._finishing_ripple = False
|
||||
self._fading_out = False
|
||||
|
@ -378,7 +387,7 @@ class CommonRipple:
|
|||
if self.ripple_color:
|
||||
pass
|
||||
elif hasattr(self, "theme_cls"):
|
||||
self.ripple_color = self.theme_cls.ripple_color
|
||||
self.ripple_color = self.theme_cls.rippleColor
|
||||
else:
|
||||
# If no theme, set Gray 300.
|
||||
self.ripple_color = [
|
||||
|
@ -429,7 +438,11 @@ class RectangularRippleBehavior(CommonRipple):
|
|||
"""
|
||||
|
||||
def lay_canvas_instructions(self) -> None:
|
||||
if self._no_ripple_effect:
|
||||
"""
|
||||
Adds graphic instructions to the canvas to implement ripple animation.
|
||||
"""
|
||||
|
||||
if not self.ripple_effect:
|
||||
return
|
||||
|
||||
with self.canvas.after if self.ripple_canvas_after else self.canvas.before:
|
||||
|
@ -493,7 +506,7 @@ class CircularRippleBehavior(CommonRipple):
|
|||
"""
|
||||
|
||||
def lay_canvas_instructions(self) -> None:
|
||||
if self._no_ripple_effect:
|
||||
if not self.ripple_effect:
|
||||
return
|
||||
|
||||
with self.canvas.after if self.ripple_canvas_after else self.canvas.before:
|
||||
|
|
|
@ -0,0 +1,537 @@
|
|||
# TODO: Add docs.
|
||||
|
||||
"""
|
||||
Behaviors/State Layer
|
||||
=====================
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Material Design spec, State layers <https://m3.material.io/foundations/interaction/states/state-layers>`_
|
||||
"""
|
||||
|
||||
from kivy import platform
|
||||
from kivy.lang import Builder
|
||||
from kivy.properties import ColorProperty, NumericProperty
|
||||
|
||||
from kivymd.uix.behaviors.focus_behavior import FocusBehavior
|
||||
|
||||
|
||||
Builder.load_string(
|
||||
"""
|
||||
<StateLayerBehavior>
|
||||
canvas.after:
|
||||
Color
|
||||
rgba: self.state_layer_color
|
||||
RoundedRectangle:
|
||||
group: "State_layer_instruction"
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: self.radius if hasattr(self, "radius") else [0, ]
|
||||
""",
|
||||
filename="StateLayerBehavior.kv",
|
||||
)
|
||||
|
||||
# TODO: Add methods `set_text_color` and `set_icon_color`
|
||||
# (methods that set the color of text and icons in the state
|
||||
# `on_enter` and `on_leave` and `pressed`).
|
||||
|
||||
|
||||
class StateLayerBehavior(FocusBehavior):
|
||||
state_layer_color = ColorProperty([0, 0, 0, 0])
|
||||
"""
|
||||
The color of the layer state.
|
||||
|
||||
:attr:`state_layer_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[0, 0, 0, 0]`.
|
||||
"""
|
||||
|
||||
state_hover = NumericProperty(0.08)
|
||||
"""
|
||||
The transparency level of the layer as a percentage when hovering.
|
||||
|
||||
:attr:`state_hover` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.08`.
|
||||
"""
|
||||
|
||||
state_press = NumericProperty(0.12)
|
||||
"""
|
||||
The transparency level of the layer as a percentage when pressed.
|
||||
|
||||
:attr:`state_press` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.12`.
|
||||
"""
|
||||
|
||||
state_drag = NumericProperty(0.16)
|
||||
"""
|
||||
The transparency level of the layer as a percentage when dragged.
|
||||
|
||||
:attr:`state_drag` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.16`.
|
||||
"""
|
||||
|
||||
# The transparency value of the disabled state.
|
||||
# These values are specified in the M3 specification.
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDIconButton
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# Filled.
|
||||
icon_button_filled_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
icon_button_filled_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
|
||||
# Tonal.
|
||||
icon_button_tonal_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
icon_button_tonal_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
|
||||
# Outlined.
|
||||
icon_button_outlined_opacity_value_disabled_container = NumericProperty(
|
||||
0.12
|
||||
)
|
||||
icon_button_outlined_opacity_value_disabled_line = NumericProperty(0.12)
|
||||
icon_button_outlined_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
|
||||
# Standard.
|
||||
icon_button_standard_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDFabButton
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
fab_button_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
fab_button_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDButton
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# Filled.
|
||||
button_filled_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
button_filled_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
button_filled_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
|
||||
# Tonal.
|
||||
button_tonal_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
button_tonal_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
button_tonal_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
|
||||
# Outlined.
|
||||
button_outlined_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
button_outlined_opacity_value_disabled_line = NumericProperty(0.12)
|
||||
button_outlined_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
button_outlined_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
|
||||
# Elevated.
|
||||
button_elevated_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
button_elevated_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
button_elevated_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
|
||||
# Text.
|
||||
button_text_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
button_text_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDLabel
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
label_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDCard
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
card_filled_opacity_value_disabled_state_container = NumericProperty(0.38)
|
||||
card_outlined_opacity_value_disabled_state_container = NumericProperty(0.12)
|
||||
card_opacity_value_disabled_state_elevated_container = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDSegmentedButton
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
segmented_button_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
segmented_button_opacity_value_disabled_container_active = NumericProperty(
|
||||
0.38
|
||||
)
|
||||
segmented_button_opacity_value_disabled_line = NumericProperty(0.12)
|
||||
segmented_button_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
segmented_button_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDChip
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
chip_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
chip_opacity_value_disabled_text = NumericProperty(0.38)
|
||||
chip_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDSwitch
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
switch_opacity_value_disabled_line = NumericProperty(0.12)
|
||||
switch_opacity_value_disabled_container = NumericProperty(0.12)
|
||||
switch_thumb_opacity_value_disabled_container = NumericProperty(0.38)
|
||||
switch_opacity_value_disabled_icon = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDCheckbox
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
checkbox_opacity_value_disabled_container = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# List
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
list_opacity_value_disabled_container = NumericProperty(0.38)
|
||||
list_opacity_value_disabled_leading_avatar = NumericProperty(0.38)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# MDTextField
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
text_field_filled_opacity_value_disabled_state_container = NumericProperty(
|
||||
0.18
|
||||
)
|
||||
text_field_outlined_opacity_value_disabled_state_container = (
|
||||
NumericProperty(0)
|
||||
)
|
||||
text_field_opacity_value_disabled_max_length_label = NumericProperty(0.60)
|
||||
text_field_opacity_value_disabled_helper_text_label = NumericProperty(0.60)
|
||||
text_field_opacity_value_disabled_hint_text_label = NumericProperty(0.60)
|
||||
text_field_opacity_value_disabled_leading_icon = NumericProperty(0.60)
|
||||
text_field_opacity_value_disabled_trailing_icon = NumericProperty(0.60)
|
||||
text_field_opacity_value_disabled_line = NumericProperty(0.12)
|
||||
|
||||
_state = 0.0
|
||||
_bg_color = (0, 0, 0, 0)
|
||||
_is_already_disabled = False
|
||||
_shadow_softness = [0, 0]
|
||||
_elevation_level = 0
|
||||
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(*args, **kwargs)
|
||||
|
||||
def set_properties_widget(self) -> None:
|
||||
"""Fired `on_release/on_press/on_enter/on_leave` events."""
|
||||
|
||||
if not self.disabled:
|
||||
self._restore_properties()
|
||||
self._set_state_layer_color()
|
||||
|
||||
def on_disabled(self, instance, value) -> None:
|
||||
"""Fired when the `disabled` value changes."""
|
||||
|
||||
from kivymd.uix.card import MDCard
|
||||
from kivymd.uix.button import (
|
||||
MDIconButton,
|
||||
MDButton,
|
||||
MDFabButton,
|
||||
MDExtendedFabButton,
|
||||
)
|
||||
from kivymd.uix.segmentedbutton import MDSegmentedButtonItem
|
||||
from kivymd.uix.segmentedbutton.segmentedbutton import (
|
||||
MDSegmentButtonSelectedIcon,
|
||||
)
|
||||
from kivymd.uix.selectioncontrol import MDSwitch
|
||||
from kivymd.uix.list import BaseListItem
|
||||
from kivymd.uix.textfield import MDTextField
|
||||
|
||||
if value and not self._is_already_disabled:
|
||||
self._is_already_disabled = True
|
||||
if isinstance(self, MDCard):
|
||||
self.state_layer_color = (
|
||||
{
|
||||
"filled": self.theme_cls.surfaceColor[:-1]
|
||||
+ [
|
||||
self.card_filled_opacity_value_disabled_state_container
|
||||
],
|
||||
"outlined": self.theme_cls.outlineColor[:-1]
|
||||
+ [
|
||||
self.card_outlined_opacity_value_disabled_state_container
|
||||
],
|
||||
"elevated": self.theme_cls.surfaceVariantColor[:-1]
|
||||
+ [
|
||||
self.card_opacity_value_disabled_state_elevated_container
|
||||
],
|
||||
}[self.style]
|
||||
if not self.md_bg_color_disabled
|
||||
else self.md_bg_color_disabled
|
||||
)
|
||||
elif isinstance(self, MDIconButton):
|
||||
self.state_layer_color = (
|
||||
{
|
||||
"tonal": self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [
|
||||
self.icon_button_tonal_opacity_value_disabled_container
|
||||
],
|
||||
"filled": self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [
|
||||
self.icon_button_filled_opacity_value_disabled_container
|
||||
],
|
||||
"outlined": self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [
|
||||
self.icon_button_outlined_opacity_value_disabled_container
|
||||
],
|
||||
"standard": self.theme_cls.transparentColor,
|
||||
}[self.style]
|
||||
if not self.md_bg_color_disabled
|
||||
else self.md_bg_color_disabled
|
||||
)
|
||||
elif isinstance(self, MDButton):
|
||||
self.state_layer_color = (
|
||||
{
|
||||
"elevated": self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [
|
||||
self.button_elevated_opacity_value_disabled_container
|
||||
],
|
||||
"tonal": self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [self.button_tonal_opacity_value_disabled_container],
|
||||
"filled": self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [self.button_filled_opacity_value_disabled_container],
|
||||
"outlined": self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [
|
||||
self.button_outlined_opacity_value_disabled_container
|
||||
],
|
||||
"text": self.theme_cls.transparentColor,
|
||||
}[self.style]
|
||||
if not self.md_bg_color_disabled
|
||||
else self.md_bg_color_disabled
|
||||
)
|
||||
elif isinstance(self, (MDFabButton, MDExtendedFabButton)):
|
||||
self.state_layer_color = (
|
||||
self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [self.fab_button_opacity_value_disabled_container]
|
||||
if not self.md_bg_color_disabled
|
||||
else self.md_bg_color_disabled
|
||||
)
|
||||
elif isinstance(self, MDTextField):
|
||||
if self.mode == "filled":
|
||||
self.state_layer_color = self.theme_cls.onSurfaceColor[
|
||||
:-1
|
||||
] + [
|
||||
self.text_field_filled_opacity_value_disabled_state_container
|
||||
]
|
||||
else:
|
||||
self.state_layer_color = self.theme_cls.transparentColor
|
||||
elif isinstance(self.parent, MDSegmentedButtonItem):
|
||||
self.state_layer_color = (
|
||||
self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [self.segmented_button_opacity_value_disabled_container]
|
||||
if not self.parent.md_bg_color_disabled
|
||||
else self.parent.md_bg_color_disabled
|
||||
)
|
||||
elif isinstance(self, MDSwitch):
|
||||
self.state_layer_color = (
|
||||
(
|
||||
self.theme_cls.surfaceContainerHighestColor
|
||||
if not self.active
|
||||
else self.theme_cls.onSurfaceColor
|
||||
)[:-1]
|
||||
+ [self.switch_opacity_value_disabled_container]
|
||||
if not self.md_bg_color_disabled
|
||||
else self.md_bg_color_disabled
|
||||
)
|
||||
elif isinstance(self, BaseListItem):
|
||||
self.state_layer_color = (
|
||||
self.theme_cls.onSurfaceColor[:-1]
|
||||
+ [self.list_opacity_value_disabled_container]
|
||||
if not self.md_bg_color_disabled
|
||||
else self.md_bg_color_disabled
|
||||
)
|
||||
elif not value and self._is_already_disabled:
|
||||
self.state_layer_color = self.theme_cls.transparentColor
|
||||
self._is_already_disabled = False
|
||||
|
||||
def on_enter(self) -> None:
|
||||
"""Fired when mouse enter the bbox of the widget."""
|
||||
|
||||
self._state = self.state_hover
|
||||
self.set_properties_widget()
|
||||
|
||||
def on_leave(self) -> None:
|
||||
"""Fired when the mouse goes outside the widget border."""
|
||||
|
||||
self._state = 0.0
|
||||
self.set_properties_widget()
|
||||
|
||||
def _on_release(self, *args):
|
||||
"""
|
||||
Fired when the button is released
|
||||
(i.e. the touch/click that pressed the button goes away).
|
||||
"""
|
||||
|
||||
if platform in ["android", "ios"]:
|
||||
self._state = 0.0
|
||||
self.set_properties_widget()
|
||||
else:
|
||||
self.on_enter()
|
||||
|
||||
def _on_press(self, *args):
|
||||
"""Fired when the button is pressed."""
|
||||
|
||||
self._state = self.state_press
|
||||
self.set_properties_widget()
|
||||
|
||||
def _restore_properties(self):
|
||||
if self._state == self.state_hover and self.focus_behavior:
|
||||
if hasattr(self, "elevation_level"):
|
||||
self._elevation_level = self.elevation_level
|
||||
if hasattr(self, "shadow_softness"):
|
||||
self._shadow_softness = self.shadow_softness
|
||||
if hasattr(self, "md_bg_color"):
|
||||
self._bg_color = self.md_bg_color
|
||||
elif not self._state:
|
||||
if hasattr(self, "elevation_level"):
|
||||
self.elevation_level = self._elevation_level
|
||||
if hasattr(self, "shadow_softness"):
|
||||
self.shadow_softness = self._shadow_softness
|
||||
if hasattr(self, "bg_color"):
|
||||
self.bg_color = self._md_bg_color
|
||||
|
||||
# FIXME: For some widgets, the color of the state of its elements is
|
||||
# ignored. For example, for the `MDSwitch` widget, the color of the status
|
||||
# of the `Thumb` element and the color of the icon are ignored.
|
||||
def _get_target_color(self):
|
||||
from kivymd.uix.card import MDCard
|
||||
from kivymd.uix.button import (
|
||||
MDIconButton,
|
||||
MDButton,
|
||||
MDFabButton,
|
||||
MDExtendedFabButton,
|
||||
)
|
||||
from kivymd.uix.segmentedbutton.segmentedbutton import (
|
||||
MDSegmentedButtonContainer,
|
||||
)
|
||||
from kivymd.uix.chip import MDChip
|
||||
from kivymd.uix.selectioncontrol import MDSwitch, MDCheckbox
|
||||
from kivymd.uix.list import BaseListItem
|
||||
from kivymd.uix.textfield import MDTextField
|
||||
from kivymd.uix.navigationdrawer import MDNavigationDrawerItem
|
||||
|
||||
target_color = None
|
||||
|
||||
if not self.disabled:
|
||||
self._restore_properties()
|
||||
|
||||
if isinstance(self, MDTextField):
|
||||
if self.mode == "filled":
|
||||
target_color = self.theme_cls.onSurfaceColor
|
||||
else:
|
||||
target_color = self.theme_cls.transparentColor
|
||||
elif isinstance(self, (MDCard, BaseListItem)) and not isinstance(
|
||||
self, MDNavigationDrawerItem
|
||||
):
|
||||
target_color = self.theme_cls.onSurfaceColor
|
||||
elif isinstance(self, MDNavigationDrawerItem):
|
||||
target_color = self.theme_cls.onSecondaryContainerColor
|
||||
elif isinstance(self.parent, MDSegmentedButtonContainer):
|
||||
target_color = (
|
||||
self.theme_cls.onSurfaceColor
|
||||
if not self.active
|
||||
else self.theme_cls.onSecondaryContainerColor
|
||||
)
|
||||
elif isinstance(self, MDChip):
|
||||
# Here, depending on the widget state (focus/pressed...)
|
||||
# we set the target color of the widget's layer.
|
||||
# For example:
|
||||
#
|
||||
# if self._state == self.state_press:
|
||||
# target_color = [., ., ., .]
|
||||
# else:
|
||||
# ...
|
||||
if self.type == "assist":
|
||||
target_color = self.theme_cls.onSurfaceColor
|
||||
elif self.type in ["filter", "input", "suggestion"]:
|
||||
target_color = self.theme_cls.onSurfaceVariantColor
|
||||
elif isinstance(self, MDIconButton):
|
||||
if self.style == "filled":
|
||||
target_color = self.theme_cls.onPrimaryColor
|
||||
elif self.style == "tonal":
|
||||
target_color = self.theme_cls.onSecondaryContainerColor
|
||||
elif self.style in ["outlined", "standard"]:
|
||||
target_color = self.theme_cls.onSurfaceVariantColor
|
||||
elif isinstance(self, MDButton):
|
||||
target_color = (
|
||||
self.theme_cls.onPrimaryColor
|
||||
if self.style == "filled"
|
||||
else self.theme_cls.primaryColor
|
||||
)
|
||||
elif isinstance(self, MDCheckbox):
|
||||
target_color = (
|
||||
self.theme_cls.primaryColor
|
||||
if self.active
|
||||
else self.theme_cls.onSurfaceColor
|
||||
)
|
||||
elif isinstance(self, (MDFabButton, MDExtendedFabButton)):
|
||||
target_color = self.theme_cls.onPrimaryContainerColor
|
||||
elif isinstance(self, MDSwitch):
|
||||
target_color = (
|
||||
self.theme_cls.primaryColor
|
||||
if self.active
|
||||
else self.theme_cls.onSurfaceVariantColor
|
||||
)
|
||||
else:
|
||||
target_color = self.theme_cls.onSurfaceColor
|
||||
|
||||
return target_color
|
||||
|
||||
def _set_state_layer_color(self):
|
||||
from kivymd.uix.card import MDCard
|
||||
from kivymd.uix.button import (
|
||||
MDIconButton,
|
||||
MDButton,
|
||||
MDFabButton,
|
||||
MDExtendedFabButton,
|
||||
)
|
||||
from kivymd.uix.segmentedbutton.segmentedbutton import (
|
||||
MDSegmentedButtonContainer,
|
||||
)
|
||||
from kivymd.uix.chip import MDChip
|
||||
from kivymd.uix.selectioncontrol import MDSwitch, MDCheckbox
|
||||
from kivymd.uix.list import BaseListItem
|
||||
from kivymd.uix.textfield import MDTextField
|
||||
from kivymd.uix.tab.tab import MDTabsItemBase
|
||||
|
||||
target_color = self._get_target_color()
|
||||
if (
|
||||
isinstance(
|
||||
self,
|
||||
(
|
||||
MDCard,
|
||||
MDTextField,
|
||||
MDIconButton,
|
||||
MDButton,
|
||||
MDFabButton,
|
||||
MDExtendedFabButton,
|
||||
MDChip,
|
||||
MDSwitch,
|
||||
MDCheckbox,
|
||||
BaseListItem,
|
||||
MDTabsItemBase,
|
||||
),
|
||||
)
|
||||
or isinstance(self.parent, MDSegmentedButtonContainer)
|
||||
and target_color
|
||||
):
|
||||
if self._state == self.state_hover and self.focus_behavior:
|
||||
if (
|
||||
not self.focus_color
|
||||
or self.theme_cls.dynamic_color
|
||||
and self.theme_focus_color == "Primary"
|
||||
):
|
||||
if (
|
||||
isinstance(self, MDTextField)
|
||||
and self.mode == "outlined"
|
||||
):
|
||||
self.state_layer_color = target_color
|
||||
else:
|
||||
self.state_layer_color = target_color[:-1] + [
|
||||
self._state
|
||||
]
|
||||
else:
|
||||
self.state_layer_color = self.focus_color
|
||||
elif self._state == self.state_press:
|
||||
self.state_layer_color = target_color[:-1] + [self._state]
|
||||
elif not self._state:
|
||||
self.state_layer_color = target_color[:-1] + [self._state]
|
|
@ -63,7 +63,7 @@ KivyMD
|
|||
#:import images_path kivymd.images_path
|
||||
|
||||
|
||||
MDCarousel:
|
||||
Carousel:
|
||||
|
||||
StencilImage:
|
||||
size_hint: .9, .8
|
||||
|
|
|
@ -125,25 +125,16 @@ You can inherit the ``MyToggleButton`` class only from the following classes
|
|||
- :class:`~kivymd.uix.button.MDFillRoundFlatIconButton`
|
||||
"""
|
||||
|
||||
__all__ = ("MDToggleButton",)
|
||||
__all__ = ("MDToggleButtonBehavior",)
|
||||
|
||||
from kivy import Logger
|
||||
from kivy.properties import BooleanProperty, ColorProperty
|
||||
from kivy.uix.behaviors import ToggleButtonBehavior
|
||||
|
||||
from kivymd.uix.button import (
|
||||
ButtonContentsIconText,
|
||||
MDFillRoundFlatButton,
|
||||
MDFillRoundFlatIconButton,
|
||||
MDFlatButton,
|
||||
MDRaisedButton,
|
||||
MDRectangleFlatButton,
|
||||
MDRectangleFlatIconButton,
|
||||
MDRoundFlatButton,
|
||||
MDRoundFlatIconButton,
|
||||
)
|
||||
from kivymd.uix.button import MDButton, MDIconButton, MDFabButton, BaseButton
|
||||
|
||||
|
||||
class MDToggleButton(ToggleButtonBehavior):
|
||||
class MDToggleButtonBehavior(ToggleButtonBehavior):
|
||||
background_normal = ColorProperty(None)
|
||||
"""
|
||||
Color of the button in ``rgba`` format for the 'normal' state.
|
||||
|
@ -180,38 +171,29 @@ class MDToggleButton(ToggleButtonBehavior):
|
|||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
classinfo = (
|
||||
MDRaisedButton,
|
||||
MDFlatButton,
|
||||
MDRectangleFlatButton,
|
||||
MDRectangleFlatIconButton,
|
||||
MDRoundFlatButton,
|
||||
MDRoundFlatIconButton,
|
||||
MDFillRoundFlatButton,
|
||||
MDFillRoundFlatIconButton,
|
||||
)
|
||||
classinfo = (MDButton, MDIconButton, MDFabButton)
|
||||
# Do the object inherited from the "supported" buttons?
|
||||
if not issubclass(self.__class__, classinfo):
|
||||
raise ValueError(
|
||||
f"Class {self.__class__} must be inherited from one of the "
|
||||
f"classes in the list {classinfo}"
|
||||
)
|
||||
else:
|
||||
print(666, self.md_bg_color)
|
||||
# self.theme_bg_color = "Custom"
|
||||
if (
|
||||
not self.background_normal
|
||||
): # This means that if the value == [] or None will return True.
|
||||
# If the object inherits from buttons with background:
|
||||
if isinstance(
|
||||
self,
|
||||
(
|
||||
MDRaisedButton,
|
||||
MDFillRoundFlatButton,
|
||||
MDFillRoundFlatIconButton,
|
||||
),
|
||||
):
|
||||
if isinstance(self, BaseButton):
|
||||
print(111)
|
||||
self.__is_filled = True
|
||||
self.background_normal = self.theme_cls.primary_color
|
||||
self.background_normal = (
|
||||
"yellow" # self.theme_cls.primary_color
|
||||
)
|
||||
# If not background_normal must be the same as the inherited one.
|
||||
else:
|
||||
print(222)
|
||||
self.background_normal = (
|
||||
self.md_bg_color[:] if self.md_bg_color else (0, 0, 0, 0)
|
||||
)
|
||||
|
@ -228,25 +210,44 @@ class MDToggleButton(ToggleButtonBehavior):
|
|||
# self.bind(state=self._update_bg)
|
||||
self.fbind("state", self._update_bg)
|
||||
|
||||
def _update_bg(self, ins, val):
|
||||
def _update_bg(self, instance, value):
|
||||
"""Updates the color of the background."""
|
||||
|
||||
if val == "down":
|
||||
self.md_bg_color = self.background_down
|
||||
if (
|
||||
self.__is_filled is False
|
||||
): # If the background is transparent, and the button it toggled,
|
||||
# the font color must be withe [1, 1, 1, 1].
|
||||
self.text_color = self.font_color_down
|
||||
if self.theme_bg_color == "Primary":
|
||||
self.theme_bg_color = "Custom"
|
||||
if self.theme_icon_color == "Primary":
|
||||
self.theme_icon_color = "Custom"
|
||||
|
||||
if value == "down":
|
||||
if isinstance(self, MDIconButton):
|
||||
self.md_bg_color = self.theme_cls.primaryColor
|
||||
self.icon_color = self.theme_cls.onPrimaryColor
|
||||
|
||||
# if (
|
||||
# self.__is_filled is False
|
||||
# ):
|
||||
# self.text_color = self.font_color_down
|
||||
if self.group:
|
||||
self._release_group(self)
|
||||
else:
|
||||
self.md_bg_color = self.background_normal
|
||||
if (
|
||||
self.__is_filled is False
|
||||
): # If the background is transparent, the font color must be the
|
||||
# primary color.
|
||||
self.text_color = self.font_color_normal
|
||||
if isinstance(self, MDIconButton):
|
||||
self.md_bg_color = self.theme_cls.surfaceContainerHighestColor
|
||||
self.icon_color = self.theme_cls.primaryColor
|
||||
|
||||
if issubclass(self.__class__, ButtonContentsIconText):
|
||||
self.icon_color = self.text_color
|
||||
# if (
|
||||
# self.__is_filled is False
|
||||
# ):
|
||||
# self.text_color = self.font_color_normal
|
||||
|
||||
# if issubclass(self.__class__, ButtonContentsIconText):
|
||||
# self.icon_color = self.text_color
|
||||
|
||||
|
||||
class MDToggleButton(MDToggleButtonBehavior):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
Logger.warning(
|
||||
f"KivyMD: "
|
||||
f"The `{self.__class__.__name__}` class has been deprecated. "
|
||||
f"Use the `MDToggleButtonBehavior` class instead."
|
||||
)
|
||||
|
|
|
@ -16,21 +16,32 @@ Usage
|
|||
.. code-block:: python
|
||||
|
||||
from kivy.lang import Builder
|
||||
from kivy.properties import StringProperty
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.behaviors import TouchBehavior
|
||||
from kivymd.uix.button import MDRaisedButton
|
||||
from kivymd.uix.button import MDButton
|
||||
|
||||
KV = '''
|
||||
MDScreen:
|
||||
<TouchBehaviorButton>
|
||||
style: "elevated"
|
||||
|
||||
MyButton:
|
||||
text: "PRESS ME"
|
||||
MDButtonText:
|
||||
text: root.text
|
||||
|
||||
|
||||
MDScreen:
|
||||
md_bg_color: self.theme_cls.backgroundColor
|
||||
|
||||
TouchBehaviorButton:
|
||||
text: "TouchBehavior"
|
||||
pos_hint: {"center_x": .5, "center_y": .5}
|
||||
'''
|
||||
|
||||
|
||||
class MyButton(MDRaisedButton, TouchBehavior):
|
||||
class TouchBehaviorButton(MDButton, TouchBehavior):
|
||||
text = StringProperty()
|
||||
|
||||
def on_long_touch(self, *args):
|
||||
print("<on_long_touch> event")
|
||||
|
||||
|
@ -41,12 +52,12 @@ Usage
|
|||
print("<on_triple_tap> event")
|
||||
|
||||
|
||||
class MainApp(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
return Builder.load_string(KV)
|
||||
|
||||
|
||||
MainApp().run()
|
||||
Example().run()
|
||||
"""
|
||||
|
||||
__all__ = ("TouchBehavior",)
|
||||
|
@ -85,16 +96,18 @@ class TouchBehavior:
|
|||
self.on_triple_tap(touch, *args)
|
||||
|
||||
def delete_clock(self, widget, touch, *args):
|
||||
"""Removes a key event from `touch.ud`."""
|
||||
|
||||
if self.collide_point(touch.x, touch.y):
|
||||
if "event" in touch.ud:
|
||||
Clock.unschedule(touch.ud["event"])
|
||||
del touch.ud["event"]
|
||||
|
||||
def on_long_touch(self, touch, *args):
|
||||
"""Called when the widget is pressed for a long time."""
|
||||
"""Fired when the widget is pressed for a long time."""
|
||||
|
||||
def on_double_tap(self, touch, *args):
|
||||
"""Called by double clicking on the widget."""
|
||||
"""Fired by double-clicking on the widget."""
|
||||
|
||||
def on_triple_tap(self, touch, *args):
|
||||
"""Called by triple clicking on the widget."""
|
||||
"""Fired by triple clicking on the widget."""
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
# NOQA F401
|
||||
from .bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
|
Binary file not shown.
Binary file not shown.
|
@ -1,118 +0,0 @@
|
|||
#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT
|
||||
|
||||
|
||||
<MDBottomNavigation>
|
||||
orientation: "vertical"
|
||||
height:
|
||||
STANDARD_INCREMENT if app.theme_cls.material_style == "M2" else "80dp"
|
||||
|
||||
ScreenManager:
|
||||
id: tab_manager
|
||||
transition: root.transition(duration=root.transition_duration)
|
||||
on_current:
|
||||
root.dispatch( \
|
||||
"on_switch_tabs", \
|
||||
root._get_switchig_tab(self.current), \
|
||||
self.current \
|
||||
)
|
||||
|
||||
MDBottomNavigationBar:
|
||||
id: bottom_panel
|
||||
size_hint_y: None
|
||||
radius: root.radius
|
||||
height:
|
||||
STANDARD_INCREMENT \
|
||||
if app.theme_cls.material_style == "M2" else \
|
||||
"80dp"
|
||||
md_bg_color:
|
||||
root.theme_cls.bg_dark \
|
||||
if not root.panel_color \
|
||||
else root.panel_color
|
||||
|
||||
MDBoxLayout:
|
||||
id: tab_bar
|
||||
pos_hint: {"center_x": .5, "center_y": .5}
|
||||
size_hint: None, None
|
||||
height:
|
||||
STANDARD_INCREMENT \
|
||||
if app.theme_cls.material_style == "M2" else \
|
||||
"80dp"
|
||||
|
||||
|
||||
<MDBottomNavigationHeader>
|
||||
md_bg_color: root.panel_color
|
||||
on_press: self.tab.dispatch("on_tab_press")
|
||||
on_release: self.tab.dispatch("on_tab_release")
|
||||
on_touch_down: self.tab.dispatch("on_tab_touch_down", *args)
|
||||
on_touch_move: self.tab.dispatch("on_tab_touch_move", *args)
|
||||
on_touch_up: self.tab.dispatch("on_tab_touch_up", *args)
|
||||
width:
|
||||
root.panel.width / len(root.panel.ids.tab_manager.screens) \
|
||||
if len(root.panel.ids.tab_manager.screens) != 0 \
|
||||
else root.panel.width
|
||||
padding:
|
||||
0, "12dp", 0, "12dp" if app.theme_cls.material_style == "M2" else "16dp"
|
||||
|
||||
RelativeLayout:
|
||||
id: item_container
|
||||
|
||||
MDIcon:
|
||||
id: _label_icon
|
||||
icon: root.tab.icon
|
||||
height: self.height
|
||||
badge_icon: root.tab.badge_icon
|
||||
theme_text_color: "Custom"
|
||||
text_color: root._text_color_normal
|
||||
opposite_colors: root.opposite_colors
|
||||
pos: [self.pos[0], self.pos[1]]
|
||||
font_size: "24dp"
|
||||
y: item_container.height - self.height
|
||||
pos_hint:
|
||||
{"center_x": .5, "center_y": .5} \
|
||||
if not root.panel.use_text else \
|
||||
{"center_x": .5, "top": 1}
|
||||
on_icon:
|
||||
if self.icon not in md_icons.keys(): \
|
||||
self.size_hint = (None, None); \
|
||||
self.width = self.font_size; \
|
||||
self.height = self.font_size
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba:
|
||||
( \
|
||||
( \
|
||||
app.theme_cls.disabled_hint_text_color \
|
||||
if not root.selected_color_background else \
|
||||
root.selected_color_background \
|
||||
) \
|
||||
if root.active else \
|
||||
(0, 0, 0, 0) \
|
||||
) \
|
||||
if app.theme_cls.material_style == "M3" else \
|
||||
(0, 0, 0, 0)
|
||||
RoundedRectangle:
|
||||
radius: [16,]
|
||||
size: root._selected_region_width, dp(32)
|
||||
pos:
|
||||
self.center_x - root._selected_region_width / 2, \
|
||||
self.center_y - (dp(16))
|
||||
|
||||
MDLabel:
|
||||
id: _label
|
||||
text: root.tab.text
|
||||
size_hint_x: None
|
||||
text_size: None, root.height
|
||||
adaptive_height: True
|
||||
theme_text_color: "Custom"
|
||||
text_color: root._text_color_normal
|
||||
opposite_colors: root.opposite_colors
|
||||
font_size: root._label_font_size
|
||||
pos_hint: {"center_x": .5}
|
||||
y: -dp(4) if app.theme_cls.material_style == "M2" else 0
|
||||
font_style:
|
||||
"Button" if app.theme_cls.material_style == "M2" else "Body2"
|
||||
|
||||
|
||||
<MDTab>
|
||||
md_bg_color: root.theme_cls.bg_normal
|
|
@ -1,880 +0,0 @@
|
|||
"""
|
||||
Components/BottomNavigation
|
||||
===========================
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Material Design 2 spec, Bottom navigation <https://material.io/components/bottom-navigation>`_ and
|
||||
`Material Design 3 spec, Bottom navigation <https://m3.material.io/components/navigation-bar/overview>`_
|
||||
|
||||
.. rubric:: Bottom navigation bars allow movement between primary destinations in an app:
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation.png
|
||||
:align: center
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
<Root>
|
||||
|
||||
MDBottomNavigation:
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: "screen 1"
|
||||
|
||||
YourContent:
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: "screen 2"
|
||||
|
||||
YourContent:
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: "screen 3"
|
||||
|
||||
YourContent:
|
||||
|
||||
For ease of understanding, this code works like this:
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
<Root>
|
||||
|
||||
ScreenManager:
|
||||
|
||||
Screen:
|
||||
name: "screen 1"
|
||||
|
||||
YourContent:
|
||||
|
||||
Screen:
|
||||
name: "screen 2"
|
||||
|
||||
YourContent:
|
||||
|
||||
Screen:
|
||||
name: "screen 3"
|
||||
|
||||
YourContent:
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: Declarative KV style
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from kivy.lang import Builder
|
||||
|
||||
from kivymd.app import MDApp
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
|
||||
def build(self):
|
||||
self.theme_cls.material_style = "M3"
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
return Builder.load_string(
|
||||
'''
|
||||
MDScreen:
|
||||
|
||||
MDBottomNavigation:
|
||||
#panel_color: "#eeeaea"
|
||||
selected_color_background: "orange"
|
||||
text_color_active: "lightgrey"
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: 'screen 1'
|
||||
text: 'Mail'
|
||||
icon: 'gmail'
|
||||
badge_icon: "numeric-10"
|
||||
|
||||
MDLabel:
|
||||
text: 'Mail'
|
||||
halign: 'center'
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: 'screen 2'
|
||||
text: 'Twitter'
|
||||
icon: 'twitter'
|
||||
badge_icon: "numeric-5"
|
||||
|
||||
MDLabel:
|
||||
text: 'Twitter'
|
||||
halign: 'center'
|
||||
|
||||
MDBottomNavigationItem:
|
||||
name: 'screen 3'
|
||||
text: 'LinkedIN'
|
||||
icon: 'linkedin'
|
||||
|
||||
MDLabel:
|
||||
text: 'LinkedIN'
|
||||
halign: 'center'
|
||||
'''
|
||||
)
|
||||
|
||||
|
||||
Test().run()
|
||||
|
||||
.. tab:: Declarative python style
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
|
||||
from kivymd.uix.label import MDLabel
|
||||
from kivymd.uix.screen import MDScreen
|
||||
|
||||
|
||||
class Test(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.material_style = "M3"
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
return (
|
||||
MDScreen(
|
||||
MDBottomNavigation(
|
||||
MDBottomNavigationItem(
|
||||
MDLabel(
|
||||
text='Mail',
|
||||
halign='center',
|
||||
),
|
||||
name='screen 1',
|
||||
text='Mail',
|
||||
icon='gmail',
|
||||
badge_icon="numeric-10",
|
||||
),
|
||||
MDBottomNavigationItem(
|
||||
MDLabel(
|
||||
text='Twitter',
|
||||
halign='center',
|
||||
),
|
||||
name='screen 1',
|
||||
text='Twitter',
|
||||
icon='twitter',
|
||||
badge_icon="numeric-10",
|
||||
),
|
||||
MDBottomNavigationItem(
|
||||
MDLabel(
|
||||
text='LinkedIN',
|
||||
halign='center',
|
||||
),
|
||||
name='screen 1',
|
||||
text='LinkedIN',
|
||||
icon='linkedin',
|
||||
badge_icon="numeric-10",
|
||||
),
|
||||
selected_color_background="orange",
|
||||
text_color_active="lightgrey",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Test().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation.gif
|
||||
:align: center
|
||||
|
||||
.. rubric:: :class:`~MDBottomNavigationItem` provides the following events for use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
__events__ = (
|
||||
"on_tab_touch_down",
|
||||
"on_tab_touch_move",
|
||||
"on_tab_touch_up",
|
||||
"on_tab_press",
|
||||
"on_tab_release",
|
||||
)
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
Root:
|
||||
|
||||
MDBottomNavigation:
|
||||
|
||||
MDBottomNavigationItem:
|
||||
on_tab_touch_down: print("on_tab_touch_down")
|
||||
on_tab_touch_move: print("on_tab_touch_move")
|
||||
on_tab_touch_up: print("on_tab_touch_up")
|
||||
on_tab_press: print("on_tab_press")
|
||||
on_tab_release: print("on_tab_release")
|
||||
|
||||
YourContent:
|
||||
|
||||
How to automatically switch a tab?
|
||||
----------------------------------
|
||||
|
||||
Use method :attr:`~MDBottomNavigation.switch_tab` which takes as argument
|
||||
the name of the tab you want to switch to.
|
||||
|
||||
Use custom icon
|
||||
---------------
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBottomNavigation:
|
||||
|
||||
MDBottomNavigationItem:
|
||||
icon: "icon.png"
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-custom-icon.png
|
||||
:align: center
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
"TabbedPanelBase",
|
||||
"MDBottomNavigationItem",
|
||||
"MDBottomNavigation",
|
||||
"MDTab",
|
||||
)
|
||||
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.clock import Clock
|
||||
from kivy.core.window import Window
|
||||
from kivy.core.window.window_sdl2 import WindowSDL
|
||||
from kivy.lang import Builder
|
||||
from kivy.metrics import dp, sp
|
||||
from kivy.properties import (
|
||||
BooleanProperty,
|
||||
ColorProperty,
|
||||
ListProperty,
|
||||
NumericProperty,
|
||||
ObjectProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from kivy.uix.behaviors import ButtonBehavior
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.screenmanager import FadeTransition, ScreenManagerException
|
||||
|
||||
from kivymd import uix_path
|
||||
from kivymd.material_resources import STANDARD_INCREMENT
|
||||
from kivymd.theming import ThemableBehavior, ThemeManager
|
||||
from kivymd.uix.anchorlayout import MDAnchorLayout
|
||||
from kivymd.uix.behaviors import CommonElevationBehavior, DeclarativeBehavior
|
||||
from kivymd.uix.behaviors.backgroundcolor_behavior import (
|
||||
SpecificBackgroundColorBehavior,
|
||||
)
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.utils.set_bars_colors import set_bars_colors
|
||||
|
||||
with open(
|
||||
os.path.join(uix_path, "bottomnavigation", "bottomnavigation.kv"),
|
||||
encoding="utf-8",
|
||||
) as kv_file:
|
||||
Builder.load_string(kv_file.read())
|
||||
|
||||
|
||||
class MDBottomNavigationHeader(ButtonBehavior, MDAnchorLayout):
|
||||
"""
|
||||
Bottom navigation header class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||
:class:`~kivymd.uix.anchorlayout.MDAnchorLayout`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
panel_color = ColorProperty([1, 1, 1, 0])
|
||||
"""
|
||||
Panel color of bottom navigation in (r, g, b, a) or string format.
|
||||
|
||||
:attr:`panel_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[1, 1, 1, 0]`.
|
||||
"""
|
||||
|
||||
tab = ObjectProperty()
|
||||
"""
|
||||
:attr:`tab` is an :class:`~MDBottomNavigationItem`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
panel = ObjectProperty()
|
||||
"""
|
||||
:attr:`panel` is an :class:`~MDBottomNavigation`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
active = BooleanProperty(False)
|
||||
|
||||
text = StringProperty()
|
||||
"""
|
||||
:attr:`text` is an :class:`~MDTab.text`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
text_color_normal = ColorProperty([1, 1, 1, 1])
|
||||
"""
|
||||
Text color in (r, g, b, a) or string format of the label when it is not
|
||||
selected.
|
||||
|
||||
:attr:`text_color_normal` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
"""
|
||||
|
||||
text_color_active = ColorProperty([1, 1, 1, 1])
|
||||
"""
|
||||
Text color in (r, g, b, a) or string format of the label when it is selected.
|
||||
|
||||
:attr:`text_color_active` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
"""
|
||||
|
||||
selected_color_background = ColorProperty(None)
|
||||
"""
|
||||
The background color in (r, g, b, a) or string format of the highlighted
|
||||
item when using Material Design v3.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`selected_color_background` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
opposite_colors = BooleanProperty(True)
|
||||
|
||||
_label = ObjectProperty()
|
||||
_label_font_size = NumericProperty("12sp")
|
||||
_text_color_normal = ColorProperty([1, 1, 1, 1])
|
||||
_text_color_active = ColorProperty([1, 1, 1, 1])
|
||||
_selected_region_width = NumericProperty(dp(64))
|
||||
|
||||
def __init__(self, panel, tab):
|
||||
self.panel = panel
|
||||
self.tab = tab
|
||||
super().__init__()
|
||||
self._text_color_normal = (
|
||||
self.theme_cls.disabled_hint_text_color
|
||||
if self.text_color_normal == [1, 1, 1, 1]
|
||||
else self.text_color_normal
|
||||
)
|
||||
self._label = self.ids._label
|
||||
self._label_font_size = sp(12)
|
||||
self.theme_cls.bind(disabled_hint_text_color=self._update_theme_style)
|
||||
self.active = False
|
||||
|
||||
def on_press(self) -> None:
|
||||
"""Called when clicking on a panel item."""
|
||||
|
||||
if self.theme_cls.material_style == "M2":
|
||||
Animation(_label_font_size=sp(14), d=0.1).start(self)
|
||||
elif self.theme_cls.material_style == "M3":
|
||||
Animation(
|
||||
_selected_region_width=dp(64),
|
||||
t="in_out_sine",
|
||||
d=0,
|
||||
).start(self)
|
||||
Animation(
|
||||
_text_color_normal=self.theme_cls.primary_color
|
||||
if self.text_color_active == [1, 1, 1, 1]
|
||||
else self.text_color_active,
|
||||
d=0.1,
|
||||
).start(self)
|
||||
|
||||
def _update_theme_style(
|
||||
self, instance_theme_manager: ThemeManager, color: list
|
||||
):
|
||||
"""Called when the application theme style changes (White/Black)."""
|
||||
|
||||
if not self.active:
|
||||
self._text_color_normal = (
|
||||
color
|
||||
if self.text_color_normal == [1, 1, 1, 1]
|
||||
else self.text_color_normal
|
||||
)
|
||||
|
||||
|
||||
class MDTab(MDScreen):
|
||||
"""
|
||||
A tab is simply a screen with meta information that defines the content
|
||||
that goes in the tab header.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.screen.MDScreen` class documentation.
|
||||
"""
|
||||
|
||||
__events__ = (
|
||||
"on_tab_touch_down",
|
||||
"on_tab_touch_move",
|
||||
"on_tab_touch_up",
|
||||
"on_tab_press",
|
||||
"on_tab_release",
|
||||
)
|
||||
"""Events provided."""
|
||||
|
||||
text = StringProperty()
|
||||
"""
|
||||
Tab header text.
|
||||
|
||||
:attr:`text` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
icon = StringProperty("checkbox-blank-circle")
|
||||
"""
|
||||
Tab header icon.
|
||||
|
||||
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'checkbox-blank-circle'`.
|
||||
"""
|
||||
|
||||
badge_icon = StringProperty()
|
||||
"""
|
||||
Tab header badge icon.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`badge_icon` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.index = 0
|
||||
self.parent_widget = None
|
||||
self.register_event_type("on_tab_touch_down")
|
||||
self.register_event_type("on_tab_touch_move")
|
||||
self.register_event_type("on_tab_touch_up")
|
||||
self.register_event_type("on_tab_press")
|
||||
self.register_event_type("on_tab_release")
|
||||
|
||||
def on_tab_touch_down(self, *args):
|
||||
pass
|
||||
|
||||
def on_tab_touch_move(self, *args):
|
||||
pass
|
||||
|
||||
def on_tab_touch_up(self, *args):
|
||||
pass
|
||||
|
||||
def on_tab_press(self, *args):
|
||||
par = self.parent_widget
|
||||
if par.previous_tab is not self:
|
||||
if par.previous_tab.index > self.index:
|
||||
par.ids.tab_manager.transition.direction = "right"
|
||||
elif par.previous_tab.index < self.index:
|
||||
par.ids.tab_manager.transition.direction = "left"
|
||||
par.ids.tab_manager.current = self.name
|
||||
par.previous_tab = self
|
||||
|
||||
def on_tab_release(self, *args):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return f"<MDTab name='{self.name}', text='{self.text}'>"
|
||||
|
||||
|
||||
class MDBottomNavigationItem(MDTab):
|
||||
header = ObjectProperty()
|
||||
"""
|
||||
:attr:`header` is an :class:`~MDBottomNavigationHeader`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def animate_header(
|
||||
self, bottom_navigation_object, bottom_navigation_header_object
|
||||
) -> None:
|
||||
if bottom_navigation_object.use_text:
|
||||
Animation(_label_font_size=sp(12), d=0.1).start(
|
||||
bottom_navigation_object.previous_tab.header
|
||||
)
|
||||
Animation(
|
||||
_selected_region_width=0,
|
||||
t="in_out_sine",
|
||||
d=0,
|
||||
).start(bottom_navigation_header_object)
|
||||
Animation(
|
||||
_text_color_normal=bottom_navigation_header_object.text_color_normal
|
||||
if bottom_navigation_object.previous_tab.header.text_color_normal
|
||||
!= [1, 1, 1, 1]
|
||||
else self.theme_cls.disabled_hint_text_color,
|
||||
d=0.1,
|
||||
).start(bottom_navigation_object.previous_tab.header)
|
||||
bottom_navigation_object.previous_tab.header.active = False
|
||||
self.header.active = True
|
||||
|
||||
def on_tab_press(self, *args) -> None:
|
||||
"""Called when clicking on a panel item."""
|
||||
|
||||
bottom_navigation_object = self.parent_widget
|
||||
bottom_navigation_header_object = (
|
||||
bottom_navigation_object.previous_tab.header
|
||||
)
|
||||
|
||||
if bottom_navigation_object.previous_tab is not self:
|
||||
self.animate_header(
|
||||
bottom_navigation_object, bottom_navigation_header_object
|
||||
)
|
||||
|
||||
super().on_tab_press(*args)
|
||||
|
||||
def on_disabled(
|
||||
self, instance_bottom_navigation_item, disabled_value: bool
|
||||
) -> None:
|
||||
self.header.disabled = disabled_value
|
||||
|
||||
def on_leave(self, *args):
|
||||
pass
|
||||
|
||||
|
||||
class TabbedPanelBase(
|
||||
ThemableBehavior, SpecificBackgroundColorBehavior, BoxLayout
|
||||
):
|
||||
"""
|
||||
A class that contains all variables a :class:`~kivy.properties.TabPannel`
|
||||
must have. It is here so I (zingballyhoo) don't get mad about
|
||||
the :class:`~kivy.properties.TabbedPannels` not being DRY.
|
||||
|
||||
For more information, see in the :class:`~kivymd.theming.ThemableBehavior`
|
||||
and :class:`~kivymd.uix.behaviors.SpecificBackgroundColorBehavior`
|
||||
and :class:`~kivy.uix.boxlayout.BoxLayout` classes documentation.
|
||||
"""
|
||||
|
||||
current = StringProperty(None)
|
||||
"""
|
||||
Current tab name.
|
||||
|
||||
:attr:`current` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
previous_tab = ObjectProperty(None, aloownone=True)
|
||||
"""
|
||||
:attr:`previous_tab` is an :class:`~MDTab` and defaults to `None`.
|
||||
"""
|
||||
|
||||
panel_color = ColorProperty(None)
|
||||
"""
|
||||
Panel color of bottom navigation.
|
||||
|
||||
:attr:`panel_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
tabs = ListProperty()
|
||||
|
||||
|
||||
class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
|
||||
"""
|
||||
A bottom navigation that is implemented by delegating all items to a
|
||||
:class:`~kivy.uix.screenmanager.ScreenManager`.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.behaviors.DeclarativeBehavior` and
|
||||
:class:`~TabbedPanelBase` classes documentation.
|
||||
|
||||
:Events:
|
||||
:attr:`on_switch_tabs`
|
||||
Called when switching tabs. Returns the object of the tab to be
|
||||
opened.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
"""
|
||||
|
||||
transition = ObjectProperty(FadeTransition)
|
||||
"""
|
||||
Transition animation of bottom navigation screen manager.
|
||||
|
||||
.. versionadded:: 1.1.0
|
||||
|
||||
:attr:`transition` is an :class:`~kivy.properties.ObjectProperty`
|
||||
and defaults to `FadeTransition`.
|
||||
"""
|
||||
|
||||
transition_duration = NumericProperty(0.2)
|
||||
"""
|
||||
Duration animation of bottom navigation screen manager.
|
||||
|
||||
.. versionadded:: 1.1.0
|
||||
|
||||
:attr:`transition_duration` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `0.2`.
|
||||
"""
|
||||
|
||||
text_color_normal = ColorProperty([1, 1, 1, 1])
|
||||
"""
|
||||
Text color of the label when it is not selected.
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBottomNavigation:
|
||||
text_color_normal: 1, 0, 1, 1
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-text_color_normal.png
|
||||
|
||||
:attr:`text_color_normal` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
"""
|
||||
|
||||
text_color_active = ColorProperty([1, 1, 1, 1])
|
||||
"""
|
||||
Text color of the label when it is selected.
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBottomNavigation:
|
||||
text_color_active: 0, 0, 0, 1
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-text_color_active.png
|
||||
|
||||
:attr:`text_color_active` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `[1, 1, 1, 1]`.
|
||||
"""
|
||||
|
||||
use_text = BooleanProperty(True)
|
||||
"""
|
||||
Use text for :class:`~MDBottomNavigationItem` or not.
|
||||
If ``True``, the :class:`~MDBottomNavigation` panel height will be reduced
|
||||
by the text height.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-use-text.png
|
||||
:align: center
|
||||
|
||||
:attr:`use_text` is an :class:`~kivy.properties.BooleanProperty`
|
||||
and defaults to `True`.
|
||||
"""
|
||||
|
||||
selected_color_background = ColorProperty(None)
|
||||
"""
|
||||
The background color of the highlighted item when using Material Design v3.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBottomNavigation:
|
||||
selected_color_background: 0, 0, 1, .4
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation=selected-color-background.png
|
||||
|
||||
:attr:`selected_color_background` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
font_name = StringProperty("Roboto")
|
||||
"""
|
||||
Font name of the label.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDBottomNavigation:
|
||||
font_name: "path/to/font.ttf"
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-font-name.png
|
||||
|
||||
:attr:`font_name` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `'Roboto'`.
|
||||
"""
|
||||
|
||||
first_widget = ObjectProperty()
|
||||
"""
|
||||
:attr:`first_widget` is an :class:`~MDBottomNavigationItem`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
tab_header = ObjectProperty()
|
||||
"""
|
||||
:attr:`tab_header` is an :class:`~MDBottomNavigationHeader`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
set_bars_color = BooleanProperty(False)
|
||||
"""
|
||||
If `True` the background color of the navigation bar will be set
|
||||
automatically according to the current color of the toolbar.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
:attr:`set_bars_color` is an :class:`~kivy.properties.BooleanProperty`
|
||||
and defaults to `False`.
|
||||
"""
|
||||
|
||||
widget_index = NumericProperty(0)
|
||||
|
||||
# Text active color if it is selected.
|
||||
_active_color = ColorProperty([1, 1, 1, 1])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.previous_tab = None
|
||||
self.register_event_type("on_switch_tabs")
|
||||
super().__init__(*args, **kwargs)
|
||||
self.theme_cls.bind(material_style=self.refresh_tabs)
|
||||
Window.bind(on_resize=self.on_resize)
|
||||
Clock.schedule_once(lambda x: self.on_resize())
|
||||
Clock.schedule_once(self.set_status_bar_color)
|
||||
|
||||
def set_status_bar_color(self, interval: Union[int, float]) -> None:
|
||||
if self.set_bars_color:
|
||||
set_bars_colors(self.panel_color, None, self.theme_cls.theme_style)
|
||||
|
||||
def switch_tab(self, name_tab) -> None:
|
||||
"""Switching the tab by name."""
|
||||
|
||||
if not self.ids.tab_manager.has_screen(name_tab):
|
||||
raise ScreenManagerException(f"No Screen with name '{name_tab}'.")
|
||||
self.ids.tab_manager.get_screen(name_tab).dispatch("on_tab_press")
|
||||
count_index_screen = [
|
||||
self.ids.tab_manager.screens.index(screen)
|
||||
for screen in self.ids.tab_manager.screens
|
||||
if screen.name == name_tab
|
||||
][0]
|
||||
numbers_screens = list(range(len(self.ids.tab_manager.screens)))
|
||||
numbers_screens.reverse()
|
||||
self.ids.tab_bar.children[
|
||||
numbers_screens.index(count_index_screen)
|
||||
].dispatch("on_press")
|
||||
|
||||
def refresh_tabs(self, *args) -> None:
|
||||
"""Refresh all tabs."""
|
||||
|
||||
if self.ids:
|
||||
tab_bar = self.ids.tab_bar
|
||||
tab_bar.clear_widgets()
|
||||
tab_manager = self.ids.tab_manager
|
||||
self._active_color = self.theme_cls.primary_color
|
||||
|
||||
if self.text_color_active != [1, 1, 1, 1]:
|
||||
self._active_color = self.text_color_active
|
||||
|
||||
for tab in tab_manager.screens:
|
||||
self.tab_header = MDBottomNavigationHeader(tab=tab, panel=self)
|
||||
tab.header = self.tab_header
|
||||
tab_bar.add_widget(self.tab_header)
|
||||
|
||||
if tab is self.first_widget:
|
||||
self.tab_header._text_color_normal = self._active_color
|
||||
self.tab_header._label_font_size = sp(14)
|
||||
self.tab_header.active = True
|
||||
else:
|
||||
self.tab_header.ids._label.font_size = sp(12)
|
||||
self.tab_header._label_font_size = sp(12)
|
||||
|
||||
def on_font_name(self, instance_bottom_navigation, font_name: str) -> None:
|
||||
for tab in self.ids.tab_bar.children:
|
||||
tab.ids._label.font_name = font_name
|
||||
|
||||
def on_selected_color_background(
|
||||
self, instance_bottom_navigation, color: list
|
||||
) -> None:
|
||||
def on_selected_color_background(*args):
|
||||
for tab in self.ids.tab_bar.children:
|
||||
tab.selected_color_background = color
|
||||
|
||||
Clock.schedule_once(on_selected_color_background)
|
||||
|
||||
def on_use_text(
|
||||
self, instance_bottom_navigation, use_text_value: bool
|
||||
) -> None:
|
||||
if not use_text_value:
|
||||
for instance_bottom_navigation_header in self.ids.tab_bar.children:
|
||||
instance_bottom_navigation_header.ids.item_container.remove_widget(
|
||||
instance_bottom_navigation_header.ids._label
|
||||
)
|
||||
if self.theme_cls.material_style == "M2":
|
||||
height = dp(42)
|
||||
else:
|
||||
height = dp(80)
|
||||
self.height = height
|
||||
self.ids.bottom_panel.height = height
|
||||
self.ids.tab_bar.height = height
|
||||
else:
|
||||
if self.theme_cls.material_style == "M2":
|
||||
height = STANDARD_INCREMENT
|
||||
else:
|
||||
height = dp(80)
|
||||
self.height = height
|
||||
self.ids.bottom_panel.height = height
|
||||
self.ids.tab_bar.height = height
|
||||
|
||||
def on_text_color_normal(
|
||||
self, instance_bottom_navigation, color: list
|
||||
) -> None:
|
||||
MDBottomNavigationHeader.text_color_normal = color
|
||||
for tab in self.ids.tab_bar.children:
|
||||
if not tab.active:
|
||||
tab._text_color_normal = color
|
||||
|
||||
def on_text_color_active(
|
||||
self, instance_bottom_navigation, color: list
|
||||
) -> None:
|
||||
def on_text_color_active(*args):
|
||||
MDBottomNavigationHeader.text_color_active = color
|
||||
self.text_color_active = color
|
||||
for tab in self.ids.tab_bar.children:
|
||||
tab.text_color_active = color
|
||||
if tab.active:
|
||||
tab._text_color_normal = color
|
||||
|
||||
Clock.schedule_once(on_text_color_active)
|
||||
|
||||
def on_switch_tabs(self, bottom_navigation_item, name_tab: str) -> None:
|
||||
"""
|
||||
Called when switching tabs. Returns the object of the tab to be opened.
|
||||
"""
|
||||
|
||||
def on_size(self, *args) -> None:
|
||||
self.on_resize()
|
||||
|
||||
def on_resize(
|
||||
self,
|
||||
instance: Union[WindowSDL, None] = None,
|
||||
width: Union[int, None] = None,
|
||||
do_again: bool = True,
|
||||
) -> None:
|
||||
"""Called when the application window is resized."""
|
||||
|
||||
full_width = 0
|
||||
for tab in self.ids.tab_manager.screens:
|
||||
full_width += tab.header.width
|
||||
tab.header.text_color_normal = self.text_color_normal
|
||||
self.ids.tab_bar.width = full_width
|
||||
if do_again:
|
||||
Clock.schedule_once(lambda x: self.on_resize(do_again=False), 0.1)
|
||||
|
||||
def add_widget(self, widget, **kwargs):
|
||||
if isinstance(widget, MDBottomNavigationItem):
|
||||
self.widget_index += 1
|
||||
widget.index = self.widget_index
|
||||
widget.parent_widget = self
|
||||
self.ids.tab_manager.add_widget(widget)
|
||||
if self.widget_index == 1:
|
||||
self.previous_tab = widget
|
||||
self.first_widget = widget
|
||||
self.refresh_tabs()
|
||||
else:
|
||||
super().add_widget(widget)
|
||||
|
||||
def remove_widget(self, widget):
|
||||
if isinstance(widget, MDBottomNavigationItem):
|
||||
self.ids.tab_manager.remove_widget(widget)
|
||||
self.refresh_tabs()
|
||||
else:
|
||||
super().remove_widget(widget)
|
||||
|
||||
def _get_switchig_tab(self, name_tab: str) -> MDBottomNavigationItem:
|
||||
bottom_navigation_item = None
|
||||
for bottom_navigation_header_instance in self.ids.tab_bar.children:
|
||||
if bottom_navigation_header_instance.tab.name == name_tab:
|
||||
bottom_navigation_item = bottom_navigation_header_instance.tab
|
||||
break
|
||||
return bottom_navigation_item
|
||||
|
||||
|
||||
class MDBottomNavigationBar(CommonElevationBehavior, MDFloatLayout):
|
||||
pass
|
|
@ -1,11 +1,7 @@
|
|||
# NOQA F401
|
||||
from .bottomsheet import (
|
||||
MDBottomSheet,
|
||||
MDBottomSheetContent,
|
||||
MDBottomSheetDragHandle,
|
||||
MDBottomSheetDragHandleButton,
|
||||
MDBottomSheetDragHandleTitle,
|
||||
MDCustomBottomSheet,
|
||||
MDGridBottomSheet,
|
||||
MDListBottomSheet,
|
||||
)
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,4 @@
|
|||
<MDBottomSheetContent>
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
#:import Window kivy.core.window.Window
|
||||
|
||||
|
||||
<MDBottomSheetDragHandle>
|
||||
|
@ -10,13 +8,19 @@
|
|||
padding: "16dp", "8dp", "16dp", "16dp"
|
||||
|
||||
BottomSheetDragHandle:
|
||||
md_bg_color:
|
||||
app.theme_cls.disabled_hint_text_color \
|
||||
if not root.drag_handle_color else \
|
||||
root.drag_handle_color
|
||||
canvas:
|
||||
Color:
|
||||
rgba:
|
||||
app.theme_cls.disabled_hint_text_color \
|
||||
if not root.drag_handle_color else \
|
||||
root.drag_handle_color
|
||||
SmoothRoundedRectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
radius: [dp(4), ]
|
||||
|
||||
size_hint: None, None
|
||||
size: "32dp", "4dp"
|
||||
radius: 4
|
||||
pos_hint: {"center_x": .5}
|
||||
|
||||
BottomSheetDragHandleContainer:
|
||||
|
@ -27,16 +31,14 @@
|
|||
|
||||
<MDBottomSheet>
|
||||
orientation: "vertical"
|
||||
md_bg_color: root.bg_color if root.bg_color else app.theme_cls.bg_darkest
|
||||
radius: 16, 16, 0, 0
|
||||
radius: "16dp", "16dp", 0, 0
|
||||
padding: 0, "8dp", 0, 0
|
||||
-x: 0
|
||||
width: Window.width if Window.width <= dp(640) else dp(640)
|
||||
pos_hint: {"center_x": .5}
|
||||
y: self.height * (self.open_progress - 1)
|
||||
|
||||
MDBoxLayout:
|
||||
BoxLayout:
|
||||
id: drag_handle_container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
|
||||
MDBoxLayout:
|
||||
id: container
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,7 @@ BoxLayout
|
|||
|
||||
canvas:
|
||||
Color:
|
||||
rgba: app.theme_cls.primary_color
|
||||
rgba: app.theme_cls.primaryColor
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
|
@ -28,7 +28,7 @@ MDBoxLayout
|
|||
|
||||
MDBoxLayout:
|
||||
adaptive_height: True
|
||||
md_bg_color: app.theme_cls.primary_color
|
||||
md_bg_color: app.theme_cls.primaryColor
|
||||
|
||||
Available options are:
|
||||
----------------------
|
||||
|
@ -38,6 +38,7 @@ Available options are:
|
|||
- adaptive_size_
|
||||
|
||||
.. adaptive_height:
|
||||
|
||||
adaptive_height
|
||||
---------------
|
||||
|
||||
|
@ -53,6 +54,7 @@ Equivalent
|
|||
height: self.minimum_height
|
||||
|
||||
.. adaptive_width:
|
||||
|
||||
adaptive_width
|
||||
--------------
|
||||
|
||||
|
@ -68,6 +70,7 @@ Equivalent
|
|||
height: self.minimum_width
|
||||
|
||||
.. adaptive_size:
|
||||
|
||||
adaptive_size
|
||||
-------------
|
||||
|
||||
|
@ -89,15 +92,24 @@ from kivy.uix.boxlayout import BoxLayout
|
|||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix import MDAdaptiveWidget
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior, BackgroundColorBehavior
|
||||
|
||||
|
||||
class MDBoxLayout(
|
||||
DeclarativeBehavior, ThemableBehavior, BoxLayout, MDAdaptiveWidget
|
||||
DeclarativeBehavior,
|
||||
ThemableBehavior,
|
||||
BackgroundColorBehavior,
|
||||
BoxLayout,
|
||||
MDAdaptiveWidget,
|
||||
):
|
||||
"""
|
||||
Box layout class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivy.uix.boxlayout.BoxLayout` class documentation.
|
||||
For more information see in the
|
||||
:class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
|
||||
:class:`~kivymd.theming.ThemableBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.backgroundcolor_behavior.BackgroundColorBehavior` and
|
||||
:class:`~kivy.uix.boxlayout.BoxLayout` and
|
||||
:class:`~kivymd.uix.MDAdaptiveWidget`
|
||||
classes documentation.
|
||||
"""
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
# NOQA F401
|
||||
from .button import (
|
||||
BaseButton,
|
||||
ButtonContentsIconText,
|
||||
MDFillRoundFlatButton,
|
||||
MDFillRoundFlatIconButton,
|
||||
MDFlatButton,
|
||||
MDFloatingActionButton,
|
||||
MDFloatingActionButtonSpeedDial,
|
||||
MDButton,
|
||||
MDButtonIcon,
|
||||
MDButtonText,
|
||||
MDIconButton,
|
||||
MDRaisedButton,
|
||||
MDRectangleFlatButton,
|
||||
MDRectangleFlatIconButton,
|
||||
MDRoundFlatButton,
|
||||
MDRoundFlatIconButton,
|
||||
MDTextButton,
|
||||
MDFabButton,
|
||||
BaseButton,
|
||||
BaseFabButton,
|
||||
MDExtendedFabButton,
|
||||
MDExtendedFabButtonIcon,
|
||||
MDExtendedFabButtonText,
|
||||
)
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,225 +1,391 @@
|
|||
<BaseButton>
|
||||
canvas:
|
||||
Clear
|
||||
Color:
|
||||
group: "bg-color"
|
||||
rgba:
|
||||
self._md_bg_color \
|
||||
if not self.disabled else \
|
||||
self._md_bg_color_disabled
|
||||
RoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
source: self.source if hasattr(self, "source") else ""
|
||||
radius: [root._radius, ]
|
||||
Color:
|
||||
group: "outline-color"
|
||||
rgba:
|
||||
root._line_color \
|
||||
if not root.disabled else \
|
||||
(root._line_color_disabled or self._disabled_color)
|
||||
Line:
|
||||
width: root.line_width
|
||||
rounded_rectangle:
|
||||
( \
|
||||
self.x, self.y, self.width, self.height, \
|
||||
root._radius, root._radius, root._radius, root._radius, \
|
||||
self.height \
|
||||
)
|
||||
|
||||
<MDFabButton>
|
||||
size_hint: None, None
|
||||
anchor_x: root.halign
|
||||
anchor_y: root.valign
|
||||
_round_rad: [self._radius] * 4
|
||||
|
||||
|
||||
<ButtonContentsText>
|
||||
lbl_txt: lbl_txt
|
||||
width:
|
||||
max( \
|
||||
root._min_width, \
|
||||
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2] \
|
||||
text_size: self.size
|
||||
halign: "center"
|
||||
valign: "center"
|
||||
size:
|
||||
{ \
|
||||
"standard": ("56dp", "56dp"), \
|
||||
"small": ("40dp", "40dp"), \
|
||||
"large": ("96dp", "96dp"), \
|
||||
}[self.style]
|
||||
radius:
|
||||
{ \
|
||||
"standard": [dp(16), ], \
|
||||
"small": [dp(12), ], \
|
||||
"large": [dp(28), ], \
|
||||
}[self.style]
|
||||
shadow_radius:
|
||||
{ \
|
||||
"standard": [dp(14), ], \
|
||||
"small": [dp(10), ], \
|
||||
"large": [dp(26), ], \
|
||||
}[self.style]
|
||||
shadow_offset: 0, -1
|
||||
elevation_level:
|
||||
{ \
|
||||
"standard": 1, \
|
||||
"small": 1, \
|
||||
"large": 1, \
|
||||
}[self.style]
|
||||
shadow_color:
|
||||
( \
|
||||
self.theme_cls.shadowColor[:-1] + [.5] \
|
||||
if self.theme_shadow_color == "Primary" else \
|
||||
self.shadow_color \
|
||||
) \
|
||||
if not self.disabled else self.theme_cls.transparentColor
|
||||
icon_color:
|
||||
{ \
|
||||
"surface": self.theme_cls.onPrimaryContainerColor, \
|
||||
"secondary": self.theme_cls.onSecondaryContainerColor, \
|
||||
"tertiary": self.theme_cls.onTertiaryContainerColor \
|
||||
}[self.color_map] \
|
||||
if self.theme_icon_color == "Primary" else \
|
||||
( \
|
||||
self.icon_color \
|
||||
if self.icon_color else \
|
||||
self.theme_cls.transparentColor \
|
||||
)
|
||||
size_hint_min_x:
|
||||
max( \
|
||||
root._min_width, \
|
||||
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2] \
|
||||
)
|
||||
height:
|
||||
max( \
|
||||
root._min_height, \
|
||||
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3] \
|
||||
)
|
||||
size_hint_min_y:
|
||||
max( \
|
||||
root._min_height, \
|
||||
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3] \
|
||||
)
|
||||
|
||||
MDLabel:
|
||||
id: lbl_txt
|
||||
text: root.text
|
||||
font_size: root.font_size
|
||||
font_style: root.font_style
|
||||
halign: 'center'
|
||||
valign: 'middle'
|
||||
adaptive_size: True
|
||||
-text_size: None, None
|
||||
theme_text_color: root._theme_text_color
|
||||
text_color: root._text_color
|
||||
markup: True
|
||||
disabled: root.disabled
|
||||
opposite_colors: root.opposite_colors
|
||||
font_name: root.font_name if root.font_name else self.font_name
|
||||
|
||||
|
||||
<ButtonContentsIcon>
|
||||
lbl_ic: lbl_ic
|
||||
size: "48dp", "48dp"
|
||||
padding: "12dp" if root.icon in md_icons else (0, 0, 0, 0)
|
||||
# Backwards compatibility.
|
||||
theme_icon_color: root.theme_icon_color or root.theme_text_color
|
||||
|
||||
MDIcon:
|
||||
id: lbl_ic
|
||||
icon: root.icon
|
||||
font_size: root.icon_size if root.icon_size else self.font_size
|
||||
font_name: root.font_name if root.font_name else self.font_name
|
||||
opposite_colors: root.opposite_colors
|
||||
text_color:
|
||||
# FIXME: ValueError: None is not allowed for MDIcon.text_color.
|
||||
# This is only a temporary fix and does not fix the cause of the error.
|
||||
(root._icon_color if root._icon_color else root.theme_cls.text_color) \
|
||||
if not root.disabled else \
|
||||
root.theme_cls.disabled_hint_text_color \
|
||||
if not root.disabled_color else \
|
||||
root.disabled_color
|
||||
# Fix https://github.com/kivymd/KivyMD/issues/1448
|
||||
# TODO: Perhaps this change may affect other widgets.
|
||||
# You need to create tests.
|
||||
# on_icon:
|
||||
# if self.icon not in md_icons.keys(): self.size_hint = (1, 1)
|
||||
theme_text_color: root._theme_icon_color
|
||||
|
||||
|
||||
<ButtonContentsIconText>
|
||||
lbl_txt: lbl_txt
|
||||
lbl_ic: lbl_ic
|
||||
|
||||
width:
|
||||
max( \
|
||||
root._min_width, \
|
||||
root.padding[0] \
|
||||
+ lbl_ic.texture_size[0] \
|
||||
+ box.spacing \
|
||||
+ lbl_txt.texture_size[0] \
|
||||
+ root.padding[2] \
|
||||
)
|
||||
size_hint_min_x:
|
||||
max( \
|
||||
root._min_width, \
|
||||
root.padding[0] \
|
||||
+ lbl_ic.texture_size[0] \
|
||||
+ box.spacing \
|
||||
+ lbl_txt.texture_size[0] \
|
||||
+ root.padding[2] \
|
||||
)
|
||||
height:
|
||||
max( \
|
||||
root._min_height, \
|
||||
root.padding[1] \
|
||||
+ max(lbl_ic.texture_size[1], lbl_txt.texture_size[1]) \
|
||||
+ root.padding[3] \
|
||||
)
|
||||
size_hint_min_y:
|
||||
max( \
|
||||
root._min_height, \
|
||||
root.padding[1] \
|
||||
+ max(lbl_ic.texture_size[1], lbl_txt.texture_size[1]) \
|
||||
+ root.padding[3] \
|
||||
)
|
||||
|
||||
MDBoxLayout:
|
||||
id: box
|
||||
adaptive_size: True
|
||||
padding: 0
|
||||
spacing: "8dp"
|
||||
|
||||
MDIcon:
|
||||
id: lbl_ic
|
||||
size_hint_x: None
|
||||
pos_hint: {"center_y": .5}
|
||||
icon: root.icon
|
||||
opposite_colors: root.opposite_colors
|
||||
font_size:
|
||||
root.icon_size \
|
||||
if root.icon_size else \
|
||||
(18 / 14 * lbl_txt.font_size)
|
||||
text_color:
|
||||
root._icon_color \
|
||||
if not root.disabled else \
|
||||
root.theme_cls.disabled_hint_text_color
|
||||
theme_text_color: root._theme_icon_color
|
||||
|
||||
MDLabel:
|
||||
id: lbl_txt
|
||||
adaptive_size: True
|
||||
-text_size: None, None
|
||||
pos_hint: {"center_y": .5}
|
||||
halign: 'center'
|
||||
valign: 'middle'
|
||||
text: root.text
|
||||
font_size: root.font_size
|
||||
font_style: root.font_style
|
||||
font_name: root.font_name if root.font_name else self.font_name
|
||||
theme_text_color: root._theme_text_color
|
||||
text_color: root._text_color
|
||||
markup: True
|
||||
disabled: root.disabled
|
||||
opposite_colors: root.opposite_colors
|
||||
|
||||
|
||||
<MDTextButton>
|
||||
adaptive_size: True
|
||||
color: root.theme_cls.primary_color if not root.color else root.color
|
||||
opacity: 1
|
||||
|
||||
|
||||
<BaseFloatingBottomButton>
|
||||
theme_text_color: "Custom"
|
||||
md_bg_color: self.theme_cls.primary_color
|
||||
disabled_color:
|
||||
self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.fab_button_opacity_value_disabled_icon] \
|
||||
if not self.icon_color_disabled else self.icon_color_disabled
|
||||
theme_font_size: "Custom"
|
||||
font_size:
|
||||
{ \
|
||||
"standard": "24sp", \
|
||||
"small": "24sp", \
|
||||
"large": "36sp", \
|
||||
}[root.style]
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba:
|
||||
self.theme_cls.primary_color \
|
||||
if not self._bg_color else \
|
||||
self._bg_color
|
||||
RoundedRectangle:
|
||||
pos:
|
||||
(self.x - self._canvas_width + dp(1.5)) + self._padding_right / 2, \
|
||||
self.y - self._padding_right / 2 + dp(1.5)
|
||||
size:
|
||||
self.width + self._canvas_width - dp(3), \
|
||||
self.height + self._padding_right - dp(3)
|
||||
radius: [self.height / 2]
|
||||
|
||||
|
||||
<MDFloatingRootButton>
|
||||
theme_text_color: "Custom"
|
||||
md_bg_color: self.theme_cls.primary_color
|
||||
|
||||
|
||||
<MDFloatingLabel>
|
||||
padding_x: "8dp"
|
||||
padding_y: "8dp"
|
||||
adaptive_size: True
|
||||
theme_text_color: "Custom"
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: self.bg_color
|
||||
RoundedRectangle:
|
||||
{ \
|
||||
"surface": self.theme_cls.surfaceColor, \
|
||||
"secondary": self.theme_cls.secondaryColor, \
|
||||
"tertiary": self.theme_cls.tertiaryColor \
|
||||
}[self.color_map] \
|
||||
if self.theme_bg_color == "Primary" else \
|
||||
self.md_bg_color
|
||||
SmoothRoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: self.radius
|
||||
|
||||
|
||||
<MDExtendedFabButtonIcon>
|
||||
x: "16dp"
|
||||
icon_color:
|
||||
( \
|
||||
{ \
|
||||
"surface": self.theme_cls.onPrimaryContainerColor, \
|
||||
"secondary": self.theme_cls.onSecondaryContainerColor, \
|
||||
"tertiary": self.theme_cls.onTertiaryContainerColor \
|
||||
}[self.parent.color_map] \
|
||||
if self.theme_icon_color == "Primary" else \
|
||||
( \
|
||||
self.icon_color \
|
||||
if self.icon_color else self.theme_cls.transparentColor \
|
||||
) \
|
||||
) \
|
||||
if self.parent else self.theme_cls.transparentColor
|
||||
disabled_color:
|
||||
self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.fab_button_opacity_value_disabled_icon] \
|
||||
if not self.icon_color_disabled else self.icon_color_disabled
|
||||
pos_hint: {"center_y": .5}
|
||||
|
||||
|
||||
<MDExtendedFabButtonText>
|
||||
adaptive_width: True
|
||||
text_color:
|
||||
( \
|
||||
{ \
|
||||
"surface": self.theme_cls.onPrimaryContainerColor, \
|
||||
"secondary": self.theme_cls.onSecondaryContainerColor, \
|
||||
"tertiary": self.theme_cls.onTertiaryContainerColor \
|
||||
}[self.parent.color_map] \
|
||||
if self.theme_text_color == "Primary" else self.text_color \
|
||||
) \
|
||||
if self.parent else self.text_color
|
||||
disabled_color:
|
||||
( \
|
||||
self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.fab_button_opacity_value_disabled_icon] \
|
||||
if not self.parent.icon_color_disabled else \
|
||||
self.parent.icon_color_disabled \
|
||||
) \
|
||||
if self.parent else self.theme_cls.transparentColor
|
||||
pos_hint: {"center_y": .5}
|
||||
|
||||
|
||||
<MDExtendedFabButton>
|
||||
size_hint: None, None
|
||||
size: "56dp", "56dp"
|
||||
radius: [dp(16), ]
|
||||
shadow_radius: [dp(14), ]
|
||||
shadow_offset: 0, -1
|
||||
# shadow_softness: 2
|
||||
elevation_level: 1
|
||||
shadow_color:
|
||||
( \
|
||||
self.theme_cls.shadowColor \
|
||||
if self.theme_shadow_color == "Primary" else \
|
||||
self.shadow_color \
|
||||
) \
|
||||
if not self.disabled else self.theme_cls.transparentColor
|
||||
theme_font_size: "Custom"
|
||||
font_size: "24sp"
|
||||
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba:
|
||||
{ \
|
||||
"standard": self.theme_cls.surfaceContainerColor \
|
||||
if self.color_map == "surface" else \
|
||||
{ \
|
||||
"secondary": self.theme_cls.secondaryContainerColor, \
|
||||
"tertiary": self.theme_cls.tertiaryContainerColor \
|
||||
}[self.color_map], \
|
||||
"small": self.theme_cls.surfaceContainerHighColor \
|
||||
if self.color_map == "surface" else \
|
||||
{ \
|
||||
"secondary": self.theme_cls.secondaryContainerColor, \
|
||||
"tertiary": self.theme_cls.tertiaryColor \
|
||||
}[self.color_map], \
|
||||
"large": self.theme_cls.surfaceContainerColor \
|
||||
if self.color_map == "surface" else \
|
||||
{ \
|
||||
"secondary": self.theme_cls.secondaryContainerColor, \
|
||||
"tertiary": self.theme_cls.tertiaryColor \
|
||||
}[self.color_map], \
|
||||
}[self.style] \
|
||||
if self.theme_bg_color == "Primary" else \
|
||||
self.md_bg_color
|
||||
SmoothRoundedRectangle:
|
||||
size: self.size
|
||||
pos: 0, 0
|
||||
radius: self.radius
|
||||
|
||||
|
||||
<MDIconButton>
|
||||
canvas.before:
|
||||
Color:
|
||||
group: "md-icon-button-bg-color"
|
||||
rgba:
|
||||
( \
|
||||
{ \
|
||||
"standard": self.theme_cls.transparentColor, \
|
||||
"outlined": self.theme_cls.transparentColor, \
|
||||
"tonal": self.theme_cls.secondaryContainerColor, \
|
||||
"filled": self.theme_cls.primaryColor, \
|
||||
}[self.style] \
|
||||
if self.theme_bg_color == "Primary" else \
|
||||
self.md_bg_color \
|
||||
) \
|
||||
if not self.disabled else \
|
||||
( \
|
||||
( \
|
||||
{ \
|
||||
"standard": self.theme_cls.transparentColor, \
|
||||
"outlined": self.theme_cls.transparentColor, \
|
||||
"tonal": self.theme_cls.onSurfaceColor[:-1] \
|
||||
+ [self.icon_button_tonal_opacity_value_disabled_container], \
|
||||
"filled": self.theme_cls.onSurfaceColor[:-1] \
|
||||
+ [self.icon_button_filled_opacity_value_disabled_container], \
|
||||
}[self.style] \
|
||||
) \
|
||||
if not self.md_bg_color_disabled else self.md_bg_color_disabled \
|
||||
)
|
||||
SmoothRoundedRectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
radius: self.radius
|
||||
|
||||
radius: [self.height / 2,]
|
||||
halign: "center"
|
||||
valign: "center"
|
||||
size_hint: None, None
|
||||
size: dp(40), dp(40)
|
||||
text_size: self.size
|
||||
line_color:
|
||||
( \
|
||||
( \
|
||||
self.theme_cls.outlineColor \
|
||||
if self.theme_line_color == "Primary" else \
|
||||
( \
|
||||
self._line_color \
|
||||
if self._line_color else \
|
||||
self.line_color \
|
||||
) \
|
||||
) \
|
||||
if not self.disabled else \
|
||||
self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.icon_button_outlined_opacity_value_disabled_line] \
|
||||
) \
|
||||
if self.style == "outlined" else self.theme_cls.transparentColor
|
||||
icon_color:
|
||||
( \
|
||||
{ \
|
||||
"standard": self.theme_cls.primaryColor, \
|
||||
"tonal": self.theme_cls.onSecondaryContainerColor, \
|
||||
"filled": self.theme_cls.onPrimaryColor, \
|
||||
"outlined": self.theme_cls.onSurfaceVariantColor, \
|
||||
}[self.style] \
|
||||
if self.theme_icon_color == "Primary" else \
|
||||
( \
|
||||
self.icon_color \
|
||||
if self.icon_color else self.theme_cls.transparentColor \
|
||||
) \
|
||||
)
|
||||
disabled_color:
|
||||
{ \
|
||||
"standard": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.icon_button_standard_opacity_value_disabled_icon], \
|
||||
"tonal": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.icon_button_tonal_opacity_value_disabled_icon], \
|
||||
"filled": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.icon_button_filled_opacity_value_disabled_icon], \
|
||||
"outlined": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.icon_button_outlined_opacity_value_disabled_icon], \
|
||||
}[self.style] \
|
||||
if not self.icon_color_disabled else self.icon_color_disabled
|
||||
|
||||
|
||||
<MDButton>
|
||||
md_bg_color:
|
||||
{ \
|
||||
"elevated": self.theme_cls.surfaceContainerLowColor, \
|
||||
"filled": self.theme_cls.primaryColor, \
|
||||
"tonal": self.theme_cls.secondaryContainerColor, \
|
||||
"outlined": self.theme_cls.transparentColor, \
|
||||
"text": self.theme_cls.transparentColor, \
|
||||
}[self.style] \
|
||||
if self.theme_bg_color == "Primary" else self.md_bg_color
|
||||
line_color:
|
||||
( \
|
||||
( \
|
||||
self.theme_cls.outlineColor \
|
||||
if not self.disabled else \
|
||||
self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_outlined_opacity_value_disabled_line] \
|
||||
) \
|
||||
if self.style == "outlined" else \
|
||||
self.theme_cls.transparentColor \
|
||||
) \
|
||||
if self.theme_line_color == "Primary" else self.line_color
|
||||
size_hint_x: None if self.theme_width == "Primary" else self.size_hint_x
|
||||
size_hint_y: None if self.theme_height == "Primary" else self.size_hint_y
|
||||
height: "40dp"
|
||||
elevation: self.elevation_levels[self.elevation_level]
|
||||
shadow_color:
|
||||
( \
|
||||
( \
|
||||
self.theme_cls.shadowColor[:-1] + [.5] \
|
||||
if self.theme_shadow_color == "Primary" else \
|
||||
self.shadow_color \
|
||||
) \
|
||||
if self.style not in ["outlined", "text"] else \
|
||||
self.theme_cls.transparentColor \
|
||||
) \
|
||||
if not self.disabled else self.theme_cls.transparentColor
|
||||
shadow_radius: self.radius
|
||||
elevation_level:
|
||||
{ \
|
||||
"elevated": 1, \
|
||||
"filled": 0, \
|
||||
"tonal": 0, \
|
||||
"outlined": 0, \
|
||||
"text": 0, \
|
||||
}[self.style]
|
||||
shadow_offset: [0, -1] if self.style == "elevated" else [0, 0]
|
||||
|
||||
|
||||
<MDButtonText>
|
||||
adaptive_size: True
|
||||
pos_hint: {"center_y": .5}
|
||||
font_style: "Label"
|
||||
role: "large"
|
||||
markup: True
|
||||
disabled: self._button.disabled if self._button else False
|
||||
text_color:
|
||||
( \
|
||||
( \
|
||||
( \
|
||||
{ \
|
||||
"elevated": self.theme_cls.primaryColor, \
|
||||
"filled": self.theme_cls.onPrimaryColor, \
|
||||
"tonal": self.theme_cls.onSecondaryContainerColor, \
|
||||
"outlined": self.theme_cls.primaryColor, \
|
||||
"text": self.theme_cls.primaryColor, \
|
||||
}[self._button.style] \
|
||||
) \
|
||||
if self._button else self.theme_cls.transparentColor \
|
||||
) \
|
||||
if self.theme_text_color == "Primary" else self.text_color \
|
||||
)
|
||||
disabled_color:
|
||||
( \
|
||||
{ \
|
||||
"elevated": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_elevated_opacity_value_disabled_text], \
|
||||
"filled": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_filled_opacity_value_disabled_text], \
|
||||
"tonal": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_tonal_opacity_value_disabled_text], \
|
||||
"outlined": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_outlined_opacity_value_disabled_text], \
|
||||
"text": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_text_opacity_value_disabled_text], \
|
||||
}[self._button.style] \
|
||||
) \
|
||||
if self._button else self.theme_cls.transparentColor
|
||||
|
||||
|
||||
<MDButtonIcon>
|
||||
size_hint: None, None
|
||||
size: "18dp", "18dp"
|
||||
theme_font_size: "Custom"
|
||||
font_size: "20sp"
|
||||
x: "16dp"
|
||||
pos_hint: {"center_y": .5}
|
||||
icon_color:
|
||||
( \
|
||||
( \
|
||||
( \
|
||||
{ \
|
||||
"elevated": self.theme_cls.primaryColor, \
|
||||
"filled": self.theme_cls.onPrimaryColor, \
|
||||
"tonal": self.theme_cls.onSecondaryContainerColor, \
|
||||
"outlined": self.theme_cls.primaryColor, \
|
||||
"text": self.theme_cls.primaryColor, \
|
||||
}[self._button.style] \
|
||||
) \
|
||||
if self._button else self.theme_cls.transparentColor \
|
||||
) \
|
||||
if self.theme_icon_color == "Primary" else \
|
||||
( \
|
||||
self.icon_color \
|
||||
if self.icon_color else \
|
||||
self.theme_cls.transparentColor \
|
||||
) \
|
||||
)
|
||||
disabled_color:
|
||||
( \
|
||||
{ \
|
||||
"elevated": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_elevated_opacity_value_disabled_icon], \
|
||||
"filled": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_filled_opacity_value_disabled_icon], \
|
||||
"tonal": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_tonal_opacity_value_disabled_icon], \
|
||||
"outlined": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_outlined_opacity_value_disabled_icon], \
|
||||
"text": self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_text_opacity_value_disabled_icon], \
|
||||
}[self._button.style] \
|
||||
if not self.icon_color_disabled else self.icon_color_disabled \
|
||||
) \
|
||||
if self._button else self.theme_cls.transparentColor
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,5 +4,4 @@ from .card import (
|
|||
MDCardSwipe,
|
||||
MDCardSwipeFrontBox,
|
||||
MDCardSwipeLayerBox,
|
||||
MDSeparator,
|
||||
)
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,9 +1,95 @@
|
|||
<MDCardSwipeLayerBox>:
|
||||
md_bg_color: app.theme_cls.divider_color
|
||||
|
||||
|
||||
<MDSeparator>
|
||||
md_bg_color:
|
||||
self.theme_cls.divider_color \
|
||||
if not root.color \
|
||||
else root.color
|
||||
app.theme_cls.secondaryContainerColor \
|
||||
if self.theme_bg_color == "Primary" else \
|
||||
self.md_bg_color
|
||||
|
||||
|
||||
<MDCard>
|
||||
shadow_radius: [(self.radius[0] / 2) + dp(2), ]
|
||||
shadow_color:
|
||||
{ \
|
||||
"filled": self.theme_cls.transparentColor, \
|
||||
"outlined": self.theme_cls.transparentColor, \
|
||||
"elevated": self.theme_cls.shadowColor \
|
||||
if self.theme_shadow_color == "Primary" else \
|
||||
self.shadow_color, \
|
||||
}[self.style] \
|
||||
if not self.disabled else \
|
||||
{ \
|
||||
"filled": self.theme_cls.transparentColor, \
|
||||
"outlined": self.theme_cls.transparentColor, \
|
||||
"elevated": self.theme_cls.shadowColor[:-1] + [.5] \
|
||||
if self.theme_shadow_color == "Primary" else \
|
||||
self.shadow_color[:-1] + [.5],
|
||||
}[self.style]
|
||||
shadow_offset:
|
||||
( \
|
||||
{ \
|
||||
"filled": [0, 0], \
|
||||
"outlined": [0, 0], \
|
||||
"elevated": [0, -2], \
|
||||
}[self.style] \
|
||||
if not self.disabled else \
|
||||
{ \
|
||||
"filled": [0, 0], \
|
||||
"outlined": [0, 0], \
|
||||
"elevated": [0, -2], \
|
||||
}[self.style] \
|
||||
) \
|
||||
if self.theme_shadow_offset == "Primary" else self.shadow_offset
|
||||
shadow_softness:
|
||||
( \
|
||||
{ \
|
||||
"filled": 0, \
|
||||
"outlined": 0, \
|
||||
"elevated": dp(4), \
|
||||
}[self.style] \
|
||||
if not self.disabled else \
|
||||
{ \
|
||||
"filled": 0, \
|
||||
"outlined": 0, \
|
||||
"elevated": dp(4), \
|
||||
}[self.style] \
|
||||
) \
|
||||
if self.theme_shadow_softness == "Primary" else self.shadow_softness
|
||||
elevation_level:
|
||||
( \
|
||||
( \
|
||||
{ \
|
||||
"filled": 0, \
|
||||
"outlined": 0, \
|
||||
"elevated": self.elevation_level if self.elevation_level else 1, \
|
||||
}[self.style] \
|
||||
) \
|
||||
if self.theme_elevation_level == "Primary" else self.elevation_level \
|
||||
) \
|
||||
if not self.disabled else \
|
||||
{ \
|
||||
"filled": 1, \
|
||||
"outlined": 0, \
|
||||
"elevated": self.elevation_level if self.elevation_level else 1, \
|
||||
}[self.style]
|
||||
elevation: self.elevation_levels[self.elevation_level]
|
||||
line_color:
|
||||
(\
|
||||
( \
|
||||
self.theme_cls.outlineColor \
|
||||
if not self.disabled else \
|
||||
self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.button_outlined_opacity_value_disabled_line] \
|
||||
) \
|
||||
if self.style == "outlined" else \
|
||||
self.theme_cls.transparentColor \
|
||||
) \
|
||||
if self.theme_line_color == "Primary" else self.line_color
|
||||
md_bg_color:
|
||||
( \
|
||||
{ \
|
||||
"filled": app.theme_cls.surfaceContainerHighestColor, \
|
||||
"outlined": app.theme_cls.surfaceColor, \
|
||||
"elevated": app.theme_cls.surfaceContainerLowColor, \
|
||||
}[self.style] \
|
||||
if self.theme_bg_color == "Primary" else \
|
||||
self.md_bg_color \
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,218 +0,0 @@
|
|||
"""
|
||||
Components/Carousel
|
||||
===================
|
||||
|
||||
:class:`~kivy.uix.boxlayout.Carousel` class equivalent. Simplifies working
|
||||
with some widget properties. For example:
|
||||
|
||||
|
||||
Carousel
|
||||
---------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
kv='''
|
||||
YourCarousel:
|
||||
BoxLayout:
|
||||
[...]
|
||||
BoxLayout:
|
||||
[...]
|
||||
BoxLayout:
|
||||
[...]
|
||||
'''
|
||||
builder.load_string(kv)
|
||||
|
||||
class YourCarousel(Carousel):
|
||||
def __init__(self,*kwargs):
|
||||
self.register_event_type("on_slide_progress")
|
||||
self.register_event_type("on_slide_complete")
|
||||
|
||||
def on_touch_down(self, *args):
|
||||
["Code to detect when the slide changes"]
|
||||
|
||||
def on_touch_up(self, *args):
|
||||
["Code to detect when the slide changes"]
|
||||
|
||||
def Calculate_slide_pos(self, *args):
|
||||
["Code to calculate the current position of the slide"]
|
||||
|
||||
def do_custom_animation(self, *args):
|
||||
["Code to recreate an animation"]
|
||||
|
||||
|
||||
MDCarousel
|
||||
-----------
|
||||
|
||||
.. code-block:: kv
|
||||
|
||||
MDCarousel:
|
||||
on_slide_progress:
|
||||
do_something()
|
||||
on_slide_complete:
|
||||
do_something()
|
||||
|
||||
"""
|
||||
# TODO: Add documentation.
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.uix.carousel import Carousel
|
||||
|
||||
from kivymd.theming import ThemableBehavior
|
||||
from kivymd.uix.behaviors import DeclarativeBehavior
|
||||
|
||||
|
||||
class MDCarousel(DeclarativeBehavior, ThemableBehavior, Carousel):
|
||||
"""
|
||||
based on kivy's carousel.
|
||||
|
||||
.. seealso::
|
||||
`kivy.uix.carousel.Carousel <https://kivy.org/doc/stable/api-kivy.uix.carousel.html>`_
|
||||
"""
|
||||
|
||||
_scrolling = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.register_event_type("on_slide_progress")
|
||||
self.register_event_type("on_slide_complete")
|
||||
|
||||
def on_slide_progress(self, *args):
|
||||
"""
|
||||
Event launched when the Slide animation is progress.
|
||||
remember to bind and unbid to this method.
|
||||
"""
|
||||
|
||||
def on_slide_complete(self, *args):
|
||||
"""
|
||||
Event launched when the Slide animation is complete.
|
||||
remember to bind and unbid to this method.
|
||||
"""
|
||||
|
||||
def _position_visible_slides(self, *args):
|
||||
slides, index = self.slides, self.index
|
||||
no_of_slides = len(slides) - 1
|
||||
if not slides:
|
||||
return
|
||||
x, y, width, height = self.x, self.y, self.width, self.height
|
||||
_offset, direction = self._offset, self.direction
|
||||
_prev, _next, _current = self._prev, self._next, self._current
|
||||
get_slide_container = self.get_slide_container
|
||||
last_slide = get_slide_container(slides[-1])
|
||||
first_slide = get_slide_container(slides[0])
|
||||
skip_next = False
|
||||
_loop = self.loop
|
||||
|
||||
if direction[0] in ["r", "l"]:
|
||||
xoff = x + _offset
|
||||
x_prev = {"l": xoff + width, "r": xoff - width}
|
||||
x_next = {"l": xoff - width, "r": xoff + width}
|
||||
if _prev:
|
||||
_prev.pos = (x_prev[direction[0]], y)
|
||||
elif _loop and _next and index == 0:
|
||||
if (_offset > 0 and direction[0] == "r") or (
|
||||
_offset < 0 and direction[0] == "l"
|
||||
):
|
||||
last_slide.pos = (x_prev[direction[0]], y)
|
||||
skip_next = True
|
||||
if _current:
|
||||
_current.pos = (xoff, y)
|
||||
|
||||
if self._scrolling:
|
||||
self.dispatch("on_slide_progress", xoff)
|
||||
|
||||
if skip_next:
|
||||
return
|
||||
if _next:
|
||||
_next.pos = (x_next[direction[0]], y)
|
||||
elif _loop and _prev and index == no_of_slides:
|
||||
if (_offset < 0 and direction[0] == "r") or (
|
||||
_offset > 0 and direction[0] == "l"
|
||||
):
|
||||
first_slide.pos = (x_next[direction[0]], y)
|
||||
if direction[0] in ["t", "b"]:
|
||||
yoff = y + _offset
|
||||
y_prev = {"t": yoff - height, "b": yoff + height}
|
||||
y_next = {"t": yoff + height, "b": yoff - height}
|
||||
if _prev:
|
||||
_prev.pos = (x, y_prev[direction[0]])
|
||||
elif _loop and _next and index == 0:
|
||||
if (_offset > 0 and direction[0] == "t") or (
|
||||
_offset < 0 and direction[0] == "b"
|
||||
):
|
||||
last_slide.pos = (x, y_prev[direction[0]])
|
||||
skip_next = True
|
||||
if _current:
|
||||
_current.pos = (x, yoff)
|
||||
if skip_next:
|
||||
return
|
||||
if _next:
|
||||
_next.pos = (x, y_next[direction[0]])
|
||||
elif _loop and _prev and index == no_of_slides:
|
||||
if (_offset < 0 and direction[0] == "t") or (
|
||||
_offset > 0 and direction[0] == "b"
|
||||
):
|
||||
first_slide.pos = (x, y_next[direction[0]])
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
self._scrolling = True
|
||||
return super().on_touch_down(touch)
|
||||
|
||||
def on_touch_up(self, touch):
|
||||
self._scrolling = False
|
||||
return super().on_touch_up(touch)
|
||||
|
||||
def _start_animation(self, *args, **kwargs):
|
||||
# compute target offset for ease back, next or prev
|
||||
new_offset = 0
|
||||
direction = kwargs.get("direction", self.direction)[0]
|
||||
is_horizontal = direction in "rl"
|
||||
extent = self.width if is_horizontal else self.height
|
||||
min_move = kwargs.get("min_move", self.min_move)
|
||||
_offset = kwargs.get("offset", self._offset)
|
||||
|
||||
if _offset < min_move * -extent:
|
||||
new_offset = -extent
|
||||
elif _offset > min_move * extent:
|
||||
new_offset = extent
|
||||
|
||||
# if new_offset is 0, it wasnt enough to go next/prev
|
||||
dur = self.anim_move_duration
|
||||
if new_offset == 0:
|
||||
dur = self.anim_cancel_duration
|
||||
|
||||
# detect edge cases if not looping
|
||||
len_slides = len(self.slides)
|
||||
index = self.index
|
||||
if not self.loop or len_slides == 1:
|
||||
is_first = index == 0
|
||||
is_last = index == len_slides - 1
|
||||
if direction in "rt":
|
||||
towards_prev = new_offset > 0
|
||||
towards_next = new_offset < 0
|
||||
else:
|
||||
towards_prev = new_offset < 0
|
||||
towards_next = new_offset > 0
|
||||
if (is_first and towards_prev) or (is_last and towards_next):
|
||||
new_offset = 0
|
||||
|
||||
anim = Animation(_offset=new_offset, d=dur, t=self.anim_type)
|
||||
anim.cancel_all(self)
|
||||
|
||||
def _cmp(*args):
|
||||
self.dispatch(
|
||||
"on_slide_complete",
|
||||
self.previous_slide,
|
||||
self.current_slide,
|
||||
self.next_slide,
|
||||
)
|
||||
if self._skip_slide is not None:
|
||||
self.index = self._skip_slide
|
||||
self._skip_slide = None
|
||||
|
||||
anim.bind(
|
||||
on_complete=_cmp,
|
||||
on_progress=lambda *args: self.dispatch(
|
||||
"on_slide_progress", self._offset
|
||||
),
|
||||
)
|
||||
anim.start(self)
|
|
@ -1 +1,7 @@
|
|||
from .chip import MDChip, MDChipText # NOQA F401
|
||||
from .chip import (
|
||||
MDChip,
|
||||
MDChipText,
|
||||
MDChipLeadingIcon,
|
||||
MDChipTrailingIcon,
|
||||
MDChipLeadingAvatar,
|
||||
) # NOQA F401
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,29 +1,91 @@
|
|||
<BaseChipIcon>
|
||||
icon_color:
|
||||
( \
|
||||
{ \
|
||||
"filter": app.theme_cls.onSurfaceVariantColor, \
|
||||
"suggestion": app.theme_cls.onSurfaceVariantColor, \
|
||||
"input": app.theme_cls.onSurfaceVariantColor, \
|
||||
"assist": app.theme_cls.primaryColor, \
|
||||
}[self._type] \
|
||||
if self.theme_icon_color == "Primary" else self.icon_color \
|
||||
) \
|
||||
if not root.disabled else self.disabled_color
|
||||
disabled_color:
|
||||
{ \
|
||||
"filter": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_icon], \
|
||||
"suggestion": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_icon], \
|
||||
"input": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_icon], \
|
||||
"assist": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_icon], \
|
||||
}[self._type] \
|
||||
if not self.icon_color_disabled else self.icon_color_disabled
|
||||
|
||||
|
||||
<MDChipText>
|
||||
font_style: "Label"
|
||||
role: "large"
|
||||
text_color:
|
||||
( \
|
||||
{ \
|
||||
"filter": app.theme_cls.onSurfaceVariantColor, \
|
||||
"suggestion": app.theme_cls.onSurfaceVariantColor, \
|
||||
"input": app.theme_cls.onSurfaceVariantColor, \
|
||||
"assist": app.theme_cls.onSurfaceColor, \
|
||||
}[self._type] \
|
||||
if root.theme_text_color == "Primary" else root.text_color \
|
||||
) \
|
||||
if not root.disabled else self.disabled_color
|
||||
disabled_color:
|
||||
{ \
|
||||
"filter": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_text], \
|
||||
"suggestion": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_text], \
|
||||
"input": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_text], \
|
||||
"assist": app.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_text], \
|
||||
}[self._type] \
|
||||
if not self.text_color_disabled else self.text_color_disabled
|
||||
|
||||
|
||||
<MDChip>
|
||||
size_hint_y: None
|
||||
height: "32dp"
|
||||
adaptive_width: True
|
||||
radius:
|
||||
16 \
|
||||
dp(16) \
|
||||
if self.radius == [0, 0, 0, 0] else \
|
||||
(max(self.radius) if max(self.radius) < self.height / 2 else 16)
|
||||
md_bg_color:
|
||||
( \
|
||||
( \
|
||||
app.theme_cls.bg_darkest \
|
||||
if app.theme_cls.theme_style == "Light" else \
|
||||
app.theme_cls.bg_light \
|
||||
) \
|
||||
if not self._origin_md_bg_color else \
|
||||
self._origin_md_bg_color
|
||||
) \
|
||||
if not self.disabled else app.theme_cls.disabled_primary_color
|
||||
(max(self.radius) if max(self.radius) < self.height / 2 else dp(16))
|
||||
line_color:
|
||||
app.theme_cls.disabled_hint_text_color \
|
||||
if self.disabled else ( \
|
||||
self._origin_line_color \
|
||||
if self._origin_line_color else \
|
||||
self.line_color \
|
||||
( \
|
||||
( \
|
||||
self.theme_cls.outlineColor \
|
||||
if not self.disabled else \
|
||||
self.theme_cls.onSurfaceColor[:-1] + \
|
||||
[self.chip_opacity_value_disabled_container] \
|
||||
) \
|
||||
if self.type != "filter" else \
|
||||
self.theme_cls.transparentColor \
|
||||
) \
|
||||
if self.theme_line_color == "Primary" else \
|
||||
self._line_color if not self.disabled else \
|
||||
( \
|
||||
self.line_color_disabled \
|
||||
if self.line_color_disabled else \
|
||||
self._line_color \
|
||||
)
|
||||
md_bg_color:
|
||||
{ \
|
||||
"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
|
||||
|
||||
LeadingIconContainer:
|
||||
id: leading_icon_container
|
||||
|
|
|
@ -4,7 +4,7 @@ Components/Chip
|
|||
|
||||
.. seealso::
|
||||
|
||||
`Material Design spec, Chips <https://m3.material.io/components/chips/overview>`_
|
||||
`Material Design 3 spec, Chips <https://m3.material.io/components/chips/overview>`_
|
||||
|
||||
.. 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
|
||||
|
@ -17,6 +17,25 @@ Components/Chip
|
|||
Usage
|
||||
-----
|
||||
|
||||
.. 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
|
||||
-------
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. tab:: Declarative KV style
|
||||
|
@ -75,34 +94,6 @@ Usage
|
|||
.. 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:
|
||||
-------------------------------------------
|
||||
|
||||
|
@ -115,6 +106,7 @@ The following types of chips are available:
|
|||
- Suggestion_
|
||||
|
||||
.. Assist:
|
||||
|
||||
Assist
|
||||
------
|
||||
|
||||
|
@ -225,6 +217,7 @@ Example of assist
|
|||
:align: center
|
||||
|
||||
.. Filter:
|
||||
|
||||
Filter
|
||||
------
|
||||
|
||||
|
@ -248,18 +241,22 @@ Example of filtering
|
|||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.chip import MDChip, MDChipText
|
||||
from kivymd.uix.list import OneLineIconListItem
|
||||
from kivymd.uix.list import MDListItem
|
||||
from kivymd.icon_definitions import md_icons
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.utils import asynckivy
|
||||
|
||||
import asynckivy
|
||||
|
||||
Builder.load_string(
|
||||
'''
|
||||
<CustomOneLineIconListItem>
|
||||
|
||||
IconLeftWidget:
|
||||
MDListItemLeadingIcon:
|
||||
icon: root.icon
|
||||
|
||||
MDListItemHeadlineText:
|
||||
text: root.text
|
||||
|
||||
|
||||
<PreviewIconsScreen>
|
||||
|
||||
|
@ -270,11 +267,15 @@ Example of filtering
|
|||
|
||||
MDTextField:
|
||||
id: search_field
|
||||
hint_text: "Search icon"
|
||||
mode: "rectangle"
|
||||
icon_left: "magnify"
|
||||
mode: "outlined"
|
||||
on_text: root.set_list_md_icons(self.text, True)
|
||||
|
||||
MDTextFieldLeadingIcon:
|
||||
icon: "magnify"
|
||||
|
||||
MDTextFieldHintText:
|
||||
text: "Search icon"
|
||||
|
||||
MDBoxLayout:
|
||||
id: chip_box
|
||||
spacing: "12dp"
|
||||
|
@ -294,18 +295,19 @@ Example of filtering
|
|||
orientation: "vertical"
|
||||
'''
|
||||
)
|
||||
|
||||
|
||||
class CustomOneLineIconListItem(OneLineIconListItem):
|
||||
|
||||
|
||||
class CustomOneLineIconListItem(MDListItem):
|
||||
icon = StringProperty()
|
||||
|
||||
|
||||
text = 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)
|
||||
|
@ -318,7 +320,7 @@ Example of filtering
|
|||
)
|
||||
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())
|
||||
|
||||
def set_filter(self, active: bool, tag: str) -> None:
|
||||
|
@ -358,7 +360,6 @@ Example of filtering
|
|||
|
||||
def build(self) -> PreviewIconsScreen:
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "LightGreen"
|
||||
return self.screen
|
||||
|
||||
def on_start(self) -> None:
|
||||
|
@ -380,7 +381,8 @@ Tap a chip to select it. Multiple chips can be selected or unselected:
|
|||
from kivymd.app import MDApp
|
||||
from kivymd.uix.chip import MDChip, MDChipText
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.utils import asynckivy
|
||||
|
||||
import asynckivy
|
||||
|
||||
Builder.load_string(
|
||||
'''
|
||||
|
@ -402,10 +404,12 @@ Tap a chip to select it. Multiple chips can be selected or unselected:
|
|||
|
||||
MDWidget:
|
||||
|
||||
MDFlatButton:
|
||||
text: "Uncheck chips"
|
||||
MDButton:
|
||||
pos: "20dp", "20dp"
|
||||
on_release: root.unchecks_chips()
|
||||
|
||||
MDButtonText:
|
||||
text: "Uncheck chips"
|
||||
'''
|
||||
)
|
||||
|
||||
|
@ -442,7 +446,6 @@ Tap a chip to select it. Multiple chips can be selected or unselected:
|
|||
|
||||
def build(self) -> ChipScreen:
|
||||
self.theme_cls.theme_style = "Dark"
|
||||
self.theme_cls.primary_palette = "LightGreen"
|
||||
return self.screen
|
||||
|
||||
def on_start(self) -> None:
|
||||
|
@ -465,7 +468,8 @@ menus:
|
|||
from kivymd.app import MDApp
|
||||
from kivymd.uix.chip import MDChip, MDChipText
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivymd.utils import asynckivy
|
||||
|
||||
import asynckivy
|
||||
|
||||
Builder.load_string(
|
||||
'''
|
||||
|
@ -485,11 +489,6 @@ menus:
|
|||
spacing: "12dp"
|
||||
adaptive_height: True
|
||||
|
||||
MDFillRoundFlatButton:
|
||||
text: "Add to cart"
|
||||
md_bg_color: "green"
|
||||
size_hint_x: 1
|
||||
|
||||
MDWidget:
|
||||
'''
|
||||
)
|
||||
|
@ -542,6 +541,7 @@ menus:
|
|||
:align: center
|
||||
|
||||
.. Input:
|
||||
|
||||
Input
|
||||
-----
|
||||
|
||||
|
@ -595,6 +595,7 @@ Example of input
|
|||
:align: center
|
||||
|
||||
.. Suggestion:
|
||||
|
||||
Suggestion
|
||||
----------
|
||||
|
||||
|
@ -641,7 +642,7 @@ Example of suggestion
|
|||
API break
|
||||
=========
|
||||
|
||||
1.1.1 version
|
||||
1.2.0 version
|
||||
-------------
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -670,7 +671,7 @@ API break
|
|||
|
||||
Test().run()
|
||||
|
||||
1.2.0 version
|
||||
2.0.0 version
|
||||
-------------
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -724,7 +725,6 @@ from kivy.properties import (
|
|||
BooleanProperty,
|
||||
ColorProperty,
|
||||
OptionProperty,
|
||||
StringProperty,
|
||||
VariableListProperty,
|
||||
)
|
||||
from kivy.uix.behaviors import ButtonBehavior
|
||||
|
@ -738,6 +738,7 @@ from kivymd.uix.behaviors import (
|
|||
ScaleBehavior,
|
||||
TouchBehavior,
|
||||
)
|
||||
from kivymd.uix.behaviors.state_layer_behavior import StateLayerBehavior
|
||||
from kivymd.uix.boxlayout import MDBoxLayout
|
||||
from kivymd.uix.label import MDIcon, MDLabel
|
||||
|
||||
|
@ -750,6 +751,29 @@ with open(
|
|||
class BaseChipIcon(
|
||||
CircularRippleBehavior, ScaleBehavior, ButtonBehavior, MDIcon
|
||||
):
|
||||
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"]
|
||||
)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.ripple_scale = 1.5
|
||||
|
@ -760,7 +784,8 @@ class BaseChipIcon(
|
|||
# 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
|
||||
and self.theme_cls.font_styles["Icon"]["large"]["font-size"]
|
||||
== self.font_size
|
||||
):
|
||||
self.font_size = (
|
||||
"18sp"
|
||||
|
@ -792,10 +817,10 @@ 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:`~kivymd.uix.behaviors.ripple_behavior.CircularRippleBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior` and
|
||||
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||
:class:`~kivymd.uix.label.MDIcon`
|
||||
:class:`~kivymd.uix.label.label.MDIcon`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
|
@ -805,10 +830,10 @@ 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:`~kivymd.uix.behaviors.ripple_behavior.CircularRippleBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior` and
|
||||
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||
:class:`~kivymd.uix.label.MDIcon`
|
||||
:class:`~kivymd.uix.label.label.MDIcon`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
|
@ -818,10 +843,10 @@ 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:`~kivymd.uix.behaviors.ripple_behavior.CircularRippleBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior` and
|
||||
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||
:class:`~kivymd.uix.label.MDIcon`
|
||||
:class:`~kivymd.uix.label.label.MDIcon`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
|
@ -831,9 +856,24 @@ class MDChipText(MDLabel):
|
|||
Implements the label for the chip.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.label.MDLabel` classes documentation.
|
||||
:class:`~kivymd.uix.label.label.MDLabel` classes documentation.
|
||||
"""
|
||||
|
||||
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"]
|
||||
)
|
||||
|
||||
|
||||
class MDChip(
|
||||
MDBoxLayout,
|
||||
|
@ -841,16 +881,17 @@ class MDChip(
|
|||
ButtonBehavior,
|
||||
CommonElevationBehavior,
|
||||
TouchBehavior,
|
||||
StateLayerBehavior,
|
||||
):
|
||||
"""
|
||||
Chip class.
|
||||
|
||||
For more information, see in the
|
||||
:class:`~kivymd.uix.boxlayout.MDBoxLayout` and
|
||||
:class:`~kivymd.uix.behaviors.RectangularRippleBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.ripple_behavior.RectangularRippleBehavior` and
|
||||
:class:`~kivy.uix.behaviors.ButtonBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.CommonElevationBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.TouchBehavior`
|
||||
:class:`~kivymd.uix.behaviors.elevation.CommonElevationBehavior` and
|
||||
:class:`~kivymd.uix.behaviors.touch_behavior.TouchBehavior`
|
||||
classes documentation.
|
||||
"""
|
||||
|
||||
|
@ -862,23 +903,13 @@ class MDChip(
|
|||
and defaults to `[dp(8), dp(8), dp(8), dp(8)]`.
|
||||
"""
|
||||
|
||||
text = StringProperty(deprecated=True)
|
||||
"""
|
||||
Chip text.
|
||||
|
||||
.. deprecated:: 1.2.0
|
||||
|
||||
:attr:`text` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
type = OptionProperty(
|
||||
"suggestion", options=["assist", "filter", "input", "suggestion"]
|
||||
)
|
||||
"""
|
||||
Type of chip.
|
||||
|
||||
.. versionadded:: 1.2.0
|
||||
.. versionadded:: 2.0.0
|
||||
|
||||
Available options are: `'assist'`, `'filter'`, `'input'`, `'suggestion'`.
|
||||
|
||||
|
@ -886,74 +917,6 @@ class MDChip(
|
|||
and defaults to `'suggestion'`.
|
||||
"""
|
||||
|
||||
icon_left = StringProperty(deprecated=True)
|
||||
"""
|
||||
Chip left icon.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. deprecated:: 1.2.0
|
||||
|
||||
:attr:`icon_left` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
icon_right = StringProperty(deprecated=True)
|
||||
"""
|
||||
Chip right icon.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. deprecated:: 1.2.0
|
||||
|
||||
:attr:`icon_right` is an :class:`~kivy.properties.StringProperty`
|
||||
and defaults to `''`.
|
||||
"""
|
||||
|
||||
text_color = ColorProperty(None, deprecated=True)
|
||||
"""
|
||||
Chip's text color in (r, g, b, a) or string format.
|
||||
|
||||
.. deprecated:: 1.2.0
|
||||
|
||||
:attr:`text_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
icon_right_color = ColorProperty(None, deprecated=True)
|
||||
"""
|
||||
Chip's right icon color in (r, g, b, a) or string format.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. deprecated:: 1.2.0
|
||||
|
||||
:attr:`icon_right_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
icon_left_color = ColorProperty(None, deprecated=True)
|
||||
"""
|
||||
Chip's left icon color in (r, g, b, a) or string format.
|
||||
|
||||
.. versionadded:: 1.0.0
|
||||
|
||||
.. deprecated:: 1.2.0
|
||||
|
||||
:attr:`icon_left_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
icon_check_color = ColorProperty(None)
|
||||
"""
|
||||
Chip's check icon color in (r, g, b, a) or string format.
|
||||
|
||||
.. 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.
|
||||
|
@ -969,12 +932,23 @@ class MDChip(
|
|||
The background color of the chip in the marked state in (r, g, b, a)
|
||||
or string format.
|
||||
|
||||
.. versionadded:: 1.2.0
|
||||
.. versionadded:: 2.0.0
|
||||
|
||||
:attr:`selected_color` is an :class:`~kivy.properties.ColorProperty`
|
||||
and defaults to `None`.
|
||||
"""
|
||||
|
||||
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)
|
||||
_current_md_bg_color = ColorProperty(None)
|
||||
# A flag that disallow ripple animation of the chip
|
||||
# at the time of clicking the chip icons.
|
||||
|
@ -986,11 +960,19 @@ class MDChip(
|
|||
super().__init__(*args, **kwargs)
|
||||
|
||||
def on_long_touch(self, *args) -> None:
|
||||
"""Fired when the widget is pressed for a long time."""
|
||||
|
||||
if self.type == "filter":
|
||||
self.active = not self.active
|
||||
|
||||
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
|
||||
|
||||
def on_type(self, instance, value: str) -> None:
|
||||
"""Called when the values of :attr:`type` change."""
|
||||
"""Fired when the values of :attr:`type` change."""
|
||||
|
||||
def adjust_padding(*args):
|
||||
"""
|
||||
|
@ -1069,7 +1051,14 @@ class MDChip(
|
|||
self.set_chip_bg_color(
|
||||
self.selected_color
|
||||
if self.selected_color
|
||||
else self.theme_cls.primary_color
|
||||
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
|
||||
)
|
||||
else:
|
||||
if (
|
||||
|
@ -1125,11 +1114,26 @@ class MDChip(
|
|||
Animation(md_bg_color=color, d=0.2).start(self)
|
||||
self._anim_complete = not self._anim_complete
|
||||
|
||||
def on_press(self, *args):
|
||||
def on_press(self, *args) -> None:
|
||||
"""Fired when the button is pressed."""
|
||||
|
||||
if self.active:
|
||||
self.active = False
|
||||
|
||||
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)
|
||||
|
||||
def add_widget(self, widget, *args, **kwargs):
|
||||
def set_type(*args):
|
||||
widget._type = self.type
|
||||
|
||||
def add_icon_leading_trailing(container):
|
||||
if len(container.children):
|
||||
type_icon = (
|
||||
|
@ -1213,6 +1217,7 @@ class MDChip(
|
|||
container.add_widget(widget)
|
||||
|
||||
if isinstance(widget, MDChipText):
|
||||
Clock.schedule_once(set_type)
|
||||
widget.adaptive_size = True
|
||||
widget.pos_hint = {"center_y": 0.5}
|
||||
if self.type == "suggestion":
|
||||
|
@ -1221,12 +1226,14 @@ class MDChip(
|
|||
lambda x: self.ids.label_container.add_widget(widget)
|
||||
)
|
||||
elif isinstance(widget, (MDChipLeadingIcon, MDChipLeadingAvatar)):
|
||||
Clock.schedule_once(set_type)
|
||||
Clock.schedule_once(
|
||||
lambda x: add_icon_leading_trailing(
|
||||
self.ids.leading_icon_container
|
||||
)
|
||||
)
|
||||
elif isinstance(widget, MDChipTrailingIcon):
|
||||
Clock.schedule_once(set_type)
|
||||
Clock.schedule_once(
|
||||
lambda x: add_icon_leading_trailing(
|
||||
self.ids.trailing_icon_container
|
||||
|
@ -1236,6 +1243,10 @@ class MDChip(
|
|||
widget,
|
||||
(LabelTextContainer, LeadingIconContainer, TrailingIconContainer),
|
||||
):
|
||||
if isinstance(
|
||||
widget, (LeadingIconContainer, TrailingIconContainer)
|
||||
):
|
||||
Clock.schedule_once(set_type)
|
||||
return super().add_widget(widget)
|
||||
|
||||
def _set_allow_chip_ripple(
|
||||
|
|
|
@ -16,7 +16,7 @@ MDCircularLayout
|
|||
|
||||
from kivymd.app import MDApp
|
||||
|
||||
kv = '''
|
||||
KV = '''
|
||||
MDScreen:
|
||||
|
||||
MDCircularLayout:
|
||||
|
@ -26,20 +26,18 @@ MDCircularLayout
|
|||
'''
|
||||
|
||||
|
||||
class Main(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
return Builder.load_string(kv)
|
||||
return Builder.load_string(KV)
|
||||
|
||||
def on_start(self):
|
||||
for x in range(1, 49):
|
||||
self.root.ids.container.add_widget(
|
||||
Label(text=f"{x}", color=[0, 0, 0, 1])
|
||||
)
|
||||
self.root.ids.container.add_widget(Label(text=f"{x}")
|
||||
|
||||
|
||||
Main().run()
|
||||
Example().run()
|
||||
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/circular-layout.png
|
||||
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/circular-layout-dark.png
|
||||
:align: center
|
||||
"""
|
||||
|
||||
|
@ -48,11 +46,10 @@ __all__ = ("MDCircularLayout",)
|
|||
from math import atan2, cos, degrees, radians, sin
|
||||
|
||||
from kivy.properties import BooleanProperty, NumericProperty
|
||||
|
||||
from kivymd.uix.floatlayout import MDFloatLayout
|
||||
from kivy.uix.floatlayout import FloatLayout
|
||||
|
||||
|
||||
class MDCircularLayout(MDFloatLayout):
|
||||
class MDCircularLayout(FloatLayout):
|
||||
degree_spacing = NumericProperty(30)
|
||||
"""
|
||||
The space between children in degree.
|
||||
|
@ -63,7 +60,8 @@ class MDCircularLayout(MDFloatLayout):
|
|||
|
||||
circular_radius = NumericProperty(None, allownone=True)
|
||||
"""
|
||||
Radius of circle. Radius will be the greatest value in the layout if `circular_radius` if not specified.
|
||||
Radius of circle. Radius will be the greatest value in the layout
|
||||
if `circular_radius` if not specified.
|
||||
|
||||
:attr:`circular_radius` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `None`.
|
||||
|
@ -79,7 +77,8 @@ class MDCircularLayout(MDFloatLayout):
|
|||
|
||||
max_degree = NumericProperty(360)
|
||||
"""
|
||||
Maximum range in degree allowed for each row of widgets before jumping to the next row.
|
||||
Maximum range in degree allowed for each row of widgets before jumping
|
||||
to the next row.
|
||||
|
||||
:attr:`max_degree` is an :class:`~kivy.properties.NumericProperty`
|
||||
and defaults to `360`.
|
||||
|
@ -185,7 +184,7 @@ if __name__ == "__main__":
|
|||
|
||||
from kivymd.app import MDApp
|
||||
|
||||
kv = """
|
||||
KV = """
|
||||
MDScreen:
|
||||
|
||||
MDCircularLayout:
|
||||
|
@ -194,14 +193,12 @@ MDScreen:
|
|||
row_spacing: min(self.size) * 0.1
|
||||
"""
|
||||
|
||||
class Main(MDApp):
|
||||
class Example(MDApp):
|
||||
def build(self):
|
||||
return Builder.load_string(kv)
|
||||
return Builder.load_string(KV)
|
||||
|
||||
def on_start(self):
|
||||
for x in range(1, 49):
|
||||
self.root.ids.container.add_widget(
|
||||
Label(text=f"{x}", color=[0, 0, 0, 1])
|
||||
)
|
||||
self.root.ids.container.add_widget(Label(text=f"{x}"))
|
||||
|
||||
Main().run()
|
||||
Example().run()
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
from .datatables import MDDataTable # NOQA F401
|
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue