Sideband/sbapp/kivymd/uix/button/button.py

1983 lines
60 KiB
Python
Raw Normal View History

2022-07-07 14:16:10 -06:00
"""
Components/Button
=================
.. seealso::
`Material Design spec, Buttons <https://material.io/components/buttons>`_
`Material Design spec, Buttons: floating action button <https://material.io/components/buttons-floating-action-button>`_
.. rubric:: Buttons allow users to take actions, and make choices,
with a single tap.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/buttons.png
:align: center
`KivyMD` provides the following button classes for use:
- MDIconButton_
- MDFloatingActionButton_
- MDFlatButton_
- MDRaisedButton_
- MDRectangleFlatButton_
- MDRectangleFlatIconButton_
- MDRoundFlatButton_
- MDRoundFlatIconButton_
- MDFillRoundFlatButton_
- MDFillRoundFlatIconButton_
- MDTextButton_
- MDFloatingActionButtonSpeedDial_
.. MDIconButton:
MDIconButton
------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-icon-button.gif
:align: center
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDIconButton:
icon: "language-python"
pos_hint: {"center_x": .5, "center_y": .5}
'''
class Example(MDApp):
def build(self):
return Builder.load_string(KV)
Example().run()
The :class:`~MDIconButton.icon` parameter must have the name of the icon
from ``kivymd/icon_definitions.py`` file.
You can also use custom icons:
.. code-block:: kv
MDIconButton:
icon: "data/logo/kivy-icon-256.png"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-icon-custom-button.gif
:align: center
By default, :class:`~MDIconButton` button has a size ``(dp(48), dp (48))``.
Use :class:`~BaseButton.icon_size` attribute to resize the button:
.. code-block:: kv
MDIconButton:
icon: "android"
icon_size: "64sp"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-icon-button-user-font-size.gif
:align: center
By default, the color of :class:`~MDIconButton`
(depending on the style of the application) is black or white.
You can change the color of :class:`~MDIconButton` as the text color
of :class:`~kivymd.uix.label.MDLabel`, substituting ``theme_icon_color`` for
``theme_text_color`` and ``icon_color`` for ``text_color``.
The use of ``user_font_size``, ``text_color`` and ``theme_text_color`` for
:class:`~MDIconButton` is deprecated.
.. code-block:: kv
MDIconButton:
icon: "android"
theme_icon_color: "Custom"
icon_color: app.theme_cls.primary_color
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-icon-button-theme-text-color.png
:align: center
.. MDFloatingActionButton:
MDFloatingActionButton
----------------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-floating-action-button.png
:align: center
The above parameters for :class:`~MDIconButton` apply
to :class:`~MDFloatingActionButton`.
To change :class:`~MDFloatingActionButton` background, use the
``md_bg_color`` parameter:
.. code-block:: kv
MDFloatingActionButton:
icon: "android"
md_bg_color: app.theme_cls.primary_color
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-floating-action-button-md-bg-color.png
:align: center
Material design style 3
-----------------------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.button import MDFloatingActionButton
KV = '''
MDScreen:
md_bg_color: "#f7f2fa"
MDBoxLayout:
id: box
spacing: "56dp"
adaptive_size: True
pos_hint: {"center_x": .5, "center_y": .5}
'''
class TestNavigationDrawer(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
return Builder.load_string(KV)
def on_start(self):
data = {
"standard": {"md_bg_color": "#fefbff", "text_color": "#6851a5"},
"small": {"md_bg_color": "#e9dff7", "text_color": "#211c29"},
"large": {"md_bg_color": "#f8d7e3", "text_color": "#311021"},
}
for type_button in data.keys():
self.root.ids.box.add_widget(
MDFloatingActionButton(
icon="pencil",
type=type_button,
theme_icon_color="Custom",
md_bg_color=data[type_button]["md_bg_color"],
icon_color=data[type_button]["text_color"],
)
)
TestNavigationDrawer().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-floating-action-button-m3.gif
:align: center
.. MDFlatButton:
MDFlatButton
------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-flat-button.gif
:align: center
To change the text color of: class:`~MDFlatButton` use the ``text_color`` parameter:
.. code-block:: kv
MDFlatButton:
text: "MDFLATBUTTON"
theme_text_color: "Custom"
text_color: 0, 0, 1, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-flat-button-text-color.png
:align: center
Or use markup:
.. code-block:: kv
MDFlatButton:
text: "[color=#00ffcc]MDFLATBUTTON[/color]"
To specify the font size and font name, use the parameters as in the usual
`Kivy` buttons:
.. code-block:: kv
MDFlatButton:
text: "MDFLATBUTTON"
font_size: "18sp"
font_name: "path/to/font"
.. MDRaisedButton:
MDRaisedButton
--------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-raised-button.gif
:align: center
This button is similar to the :class:`~MDFlatButton` button except that you
can set the background color for :class:`~MDRaisedButton`:
.. code-block:: kv
MDRaisedButton:
text: "MDRAISEDBUTTON"
md_bg_color: 1, 0, 1, 1
.. MDRectangleFlatButton:
MDRectangleFlatButton
---------------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-rectangle-flat-button.gif
:align: center
.. code-block:: kv
MDRectangleFlatButton:
text: "MDRECTANGLEFLATBUTTON"
theme_text_color: "Custom"
text_color: 1, 0, 0, 1
line_color: 0, 0, 1, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-rectangle-flat-button-md-bg-color.png
:align: center
.. MDRectangleFlatIconButton:
MDRectangleFlatIconButton
-------------------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-rectangle-flat-icon-button.png
:align: center
Button parameters :class:`~MDRectangleFlatIconButton` are the same as
button :class:`~MDRectangleFlatButton`, with the addition of the
``theme_icon_color`` and ``icon_color`` parameters as for :class:`~MDIconButton`.
.. code-block:: kv
MDRectangleFlatIconButton:
icon: "android"
text: "MDRECTANGLEFLATICONBUTTON"
theme_text_color: "Custom"
text_color: 0, 0, 1, 1
line_color: 1, 0, 1, 1
theme_icon_color: "Custom"
icon_color: 1, 0, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-rectangle-flat-icon-button-custom.png
:align: center
Without border
--------------
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.button import MDRectangleFlatIconButton
class Example(MDApp):
def build(self):
screen = MDScreen()
screen.add_widget(
MDRectangleFlatIconButton(
text="MDRectangleFlatIconButton",
icon="language-python",
line_color=(0, 0, 0, 0),
pos_hint={"center_x": .5, "center_y": .5},
)
)
return screen
Example().run()
.. code-block:: kv
MDRectangleFlatIconButton:
text: "MDRectangleFlatIconButton"
icon: "language-python"
line_color: 0, 0, 0, 0
pos_hint: {"center_x": .5, "center_y": .5}
.. MDRoundFlatButton:
MDRoundFlatButton
-----------------
.. code-block:: kv
MDRoundFlatButton:
text: "MDROUNDFLATBUTTON"
text_color: 0, 1, 0, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-round-flat-button-text-color.png
:align: center
.. MDRoundFlatIconButton:
MDRoundFlatIconButton
---------------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-round-flat-icon-button.png
:align: center
Button parameters :class:`~MDRoundFlatIconButton` are the same as
button :class:`~MDRoundFlatButton`, with the addition of the
``theme_icon_color`` and ``icon_color`` parameters as for :class:`~MDIconButton`:
.. code-block:: kv
MDRoundFlatIconButton:
icon: "android"
text: "MDROUNDFLATICONBUTTON"
.. MDFillRoundFlatButton:
MDFillRoundFlatButton
---------------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-fill-round-flat-button.png
:align: center
Button parameters :class:`~MDFillRoundFlatButton` are the same as
button :class:`~MDRaisedButton`.
.. MDFillRoundFlatIconButton:
MDFillRoundFlatIconButton
-------------------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-fill-round-flat-icon-button.png
:align: center
Button parameters :class:`~MDFillRoundFlatIconButton` are the same as
button :class:`~MDRaisedButton`, with the addition of the
``theme_icon_color`` and ``icon_color`` parameters as for :class:`~MDIconButton`.
.. note:: Notice that the width of the :class:`~MDFillRoundFlatIconButton`
button matches the size of the button text.
.. MDTextButton:
MDTextButton
------------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-text-button.png
:align: center
.. code-block:: kv
MDTextButton:
text: "MDTEXTBUTTON"
custom_color: 0, 1, 0, 1
.. MDFloatingActionButtonSpeedDial:
MDFloatingActionButtonSpeedDial
-------------------------------
.. Note:: See the full list of arguments in the class
:class:`~MDFloatingActionButtonSpeedDial`.
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDFloatingActionButtonSpeedDial:
data: app.data
root_button_anim: True
'''
class Example(MDApp):
data = {
'Python': 'language-python',
'PHP': 'language-php',
'C++': 'language-cpp',
}
def build(self):
return Builder.load_string(KV)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial.gif
:align: center
Or without KV Language:
.. code-block:: python
from kivymd.uix.screen import MDScreen
from kivymd.app import MDApp
from kivymd.uix.button import MDFloatingActionButtonSpeedDial
class Example(MDApp):
data = {
'Python': 'language-python',
'PHP': 'language-php',
'C++': 'language-cpp',
}
def build(self):
screen = MDScreen()
speed_dial = MDFloatingActionButtonSpeedDial()
speed_dial.data = self.data
speed_dial.root_button_anim = True
screen.add_widget(speed_dial)
return screen
Example().run()
You can use various types of animation of labels for buttons on the stack:
.. code-block:: kv
MDFloatingActionButtonSpeedDial:
hint_animation: True
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-hint.gif
:align: center
You can set your color values for background, text of buttons etc:
.. code-block:: kv
MDFloatingActionButtonSpeedDial:
bg_hint_color: app.theme_cls.primary_light
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-hint-color.png
:align: center
.. seealso::
`See full example <https://github.com/kivymd/KivyMD/wiki/Components-Button>`_
"""
__all__ = (
"BaseButton",
"MDIconButton",
"MDFloatingActionButton",
"MDFlatButton",
"MDRaisedButton",
"MDRectangleFlatButton",
"MDRectangleFlatIconButton",
"MDRoundFlatButton",
"MDRoundFlatIconButton",
"MDFillRoundFlatButton",
"MDFillRoundFlatIconButton",
"MDTextButton",
"MDFloatingActionButtonSpeedDial",
)
import os
from typing import Union
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.metrics import dp, sp
from kivy.properties import (
BooleanProperty,
BoundedNumericProperty,
ColorProperty,
DictProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
VariableListProperty,
)
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivymd import uix_path
from kivymd.color_definitions import text_colors
from kivymd.font_definitions import theme_font_styles
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
CommonElevationBehavior,
FakeRectangularElevationBehavior,
RectangularRippleBehavior,
RoundedRectangularElevationBehavior,
)
from kivymd.uix.label import MDLabel
from kivymd.uix.tooltip import MDTooltip
with open(
os.path.join(uix_path, "button", "button.kv"), encoding="utf-8"
) as kv_file:
Builder.load_string(kv_file.read())
theme_text_color_options = (
"Primary",
"Secondary",
"Hint",
"Error",
"Custom",
"ContrastParentBackground",
)
class BaseButton(
RectangularRippleBehavior, ThemableBehavior, ButtonBehavior, AnchorLayout
):
"""Base class for all buttons."""
padding = VariableListProperty([dp(16), dp(8), dp(16), dp(8)])
"""
Padding between the widget box and its children, in pixels:
[padding_left, padding_top, padding_right, padding_bottom].
padding also accepts a two argument form [padding_horizontal,
padding_vertical] and a one argument form [padding].
.. versionadded:: 1.0.0
:attr:`padding` is a :class:`~kivy.properties.VariableListProperty`
and defaults to [16dp, 8dp, 16dp, 8dp].
"""
halign = OptionProperty("center", options=("left", "center", "right"))
"""
Horizontal anchor.
.. versionadded:: 1.0.0
:attr:`anchor_x` is an :class:`~kivy.properties.OptionProperty`
and defaults to 'center'. It accepts values of 'left', 'center' or 'right'.
"""
valign = OptionProperty("center", options=("top", "center", "bottom"))
"""
Vertical anchor.
.. versionadded:: 1.0.0
:attr:`anchor_y` is an :class:`~kivy.properties.OptionProperty`
and defaults to 'center'. It accepts values of 'top', 'center' or 'bottom'.
"""
text = StringProperty("")
"""
Button text.
:attr:`text` is a :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
icon = StringProperty("")
"""
Button icon.
:attr:`icon` is a :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
font_style = OptionProperty("Body1", options=theme_font_styles)
"""
Button text font style.
Available vanilla font_style are: `'H1'`, `'H2'`, `'H3'`, `'H4'`, `'H5'`,
`'H6'`, `'Subtitle1'`, `'Subtitle2'`, `'Body1'`, `'Body2'`, `'Button'`,
`'Caption'`, `'Overline'`, `'Icon'`.
:attr:`font_style` is a :class:`~kivy.properties.StringProperty`
and defaults to `'Body1'`.
"""
theme_text_color = OptionProperty(None, options=theme_text_color_options)
"""
Button text type. Available options are: (`"Primary"`, `"Secondary"`,
`"Hint"`, `"Error"`, `"Custom"`, `"ContrastParentBackground"`).
:attr:`theme_text_color` is an :class:`~kivy.properties.OptionProperty`
and defaults to `None` (set by button class).
"""
theme_icon_color = OptionProperty(None, options=theme_text_color_options)
"""
Button icon type. Available options are: (`"Primary"`, `"Secondary"`,
`"Hint"`, `"Error"`, `"Custom"`, `"ContrastParentBackground"`).
.. versionadded:: 1.0.0
:attr:`theme_icon_color` is an :class:`~kivy.properties.OptionProperty`
and defaults to `None` (set by button subclass).
"""
text_color = ColorProperty(None)
"""
Button text color in (r, g, b, a) format.
:attr:`text_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
icon_color = ColorProperty(None)
"""
Button icon color in (r, g, b, a) format.
:attr:`icon_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
font_name = StringProperty()
"""
Button text font name.
:attr:`font_name` is a :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
font_size = NumericProperty("14sp")
"""
Button text font size.
:attr:`font_size` is a :class:`~kivy.properties.NumericProperty`
and defaults to `14sp`.
"""
icon_size = NumericProperty()
"""
Icon font size.
Use this parameter as the font size, that is, in sp units.
.. versionadded:: 1.0.0
:attr:`icon_size` is a :class:`~kivy.properties.NumericProperty`
and defaults to `None`.
"""
user_font_size = NumericProperty(0, deprecated=True)
"""
Custom font size for :class:`~MDIconButton`.
.. deprecated in 1.0.0::
Use :attr:`icon_size` instead.
:attr:`user_font_size` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0`.
"""
line_width = NumericProperty(1)
"""
Line width for button border.
:attr:`line_width` is a :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
line_color = ColorProperty(None)
"""
Line color for button border.
:attr:`line_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
line_color_disabled = ColorProperty(None)
"""
Disabled line color for button border.
.. versionadded:: 1.0.0
:attr:`line_color_disabled` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
md_bg_color = ColorProperty(None)
"""
Button background color.
:attr:`md_bg_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
md_bg_color_disabled = ColorProperty(None)
"""
The background color of the button when the button is disabled.
:attr:`md_bg_color_disabled` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
disabled_color = ColorProperty(None)
"""
The color of the text and icon when the button is disabled, in the
(r, g, b, a) format.
.. versionadded:: 1.0.0
:attr:`disabled_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
rounded_button = BooleanProperty(False)
"""
Should the button have fully rounded corners (e.g. like M3 buttons)?
.. versionadded:: 1.0.0
:attr:`rounded_button` is a :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
# Note - _radius must be > 0 to avoid rendering issues.
_radius = BoundedNumericProperty(dp(4), min=0.0999, errorvalue=0.1)
# Properties used for rendering.
_disabled_color = ColorProperty(None)
_md_bg_color = ColorProperty(None)
_md_bg_color_disabled = ColorProperty(None)
_line_color = ColorProperty(None)
_line_color_disabled = ColorProperty(None)
_theme_text_color = OptionProperty(None, options=theme_text_color_options)
_theme_icon_color = OptionProperty(None, options=theme_text_color_options)
_text_color = ColorProperty(None)
_icon_color = ColorProperty(None)
# Defaults which can be overridden in subclasses
_min_width = NumericProperty(dp(64))
_min_height = NumericProperty(dp(36))
# Default colors - set to None to use primary theme colors
_default_md_bg_color = [0.0, 0.0, 0.0, 0.0]
_default_md_bg_color_disabled = [0.0, 0.0, 0.0, 0.0]
_default_line_color = [0.0, 0.0, 0.0, 0.0]
_default_line_color_disabled = [0.0, 0.0, 0.0, 0.0]
_default_theme_text_color = StringProperty("Primary")
_default_theme_icon_color = StringProperty("Primary")
_default_text_color = ColorProperty(None)
_default_icon_color = ColorProperty(None)
_animation_fade_bg = ObjectProperty(None, allownone=True)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.theme_cls.bind(
primary_palette=self.set_all_colors,
theme_style=self.set_all_colors,
)
self.bind(
md_bg_color=self.set_button_colors,
md_bg_color_disabled=self.set_button_colors,
line_color=self.set_button_colors,
line_color_disabled=self.set_button_colors,
theme_text_color=self.set_text_color,
text_color=self.set_text_color,
theme_icon_color=self.set_icon_color,
icon_color=self.set_icon_color,
disabled_color=self.set_disabled_color,
rounded_button=self.set_radius,
height=self.set_radius,
)
Clock.schedule_once(self.set_all_colors)
Clock.schedule_once(self.set_radius)
def set_disabled_color(self, *args):
"""
Sets the color for the icon, text and line of the button when button
is disabled.
"""
if self.disabled:
disabled_color = (
self.disabled_color
if self.disabled_color
else self.theme_cls.disabled_hint_text_color
)
self._disabled_color = disabled_color
# Button icon color.
if "lbl_ic" in self.ids:
self.ids.lbl_ic.disabled_color = disabled_color
# Button text color.
if "lbl_txt" in self.ids:
self.ids.lbl_txt.disabled_color = disabled_color
else:
self._disabled_color = self._line_color
def set_all_colors(self, *args) -> None:
"""Set all button colours."""
self.set_button_colors()
self.set_text_color()
self.set_icon_color()
def set_button_colors(self, *args) -> None:
"""Set all button colours (except text/icons)."""
# Set main color
self._md_bg_color = (
self.md_bg_color
or self._default_md_bg_color
or self.theme_cls.primary_color
)
# Set disabled color
self._md_bg_color_disabled = (
self.md_bg_color_disabled
or (
[sum(self.md_bg_color[0:3]) / 3.0] * 3
+ [0.38 if self.theme_cls.theme_style == "Light" else 0.5]
if self.md_bg_color
else None
)
or self._default_md_bg_color_disabled
or self.theme_cls.disabled_primary_color
)
# Set line color
self._line_color = (
self.line_color
or self._default_line_color
or self.theme_cls.primary_color
)
# Set disabled line color
self._line_color_disabled = (
self.line_color_disabled
or (
[sum(self.line_color[0:3]) / 3.0] * 3
+ [0.38 if self.theme_cls.theme_style == "Light" else 0.5]
if self.line_color
else None
)
or self._default_line_color_disabled
or self.theme_cls.disabled_primary_color
)
def set_text_color(self, *args) -> None:
"""
Set _theme_text_color and _text_color based on defaults and options.
"""
self._theme_text_color = (
self.theme_text_color or self._default_theme_text_color
)
if self._default_text_color == "PrimaryHue":
default_text_color = text_colors[self.theme_cls.primary_palette][
self.theme_cls.primary_hue
]
elif self._default_text_color == "Primary":
default_text_color = self.theme_cls.primary_color
else:
default_text_color = self.theme_cls.text_color
self._text_color = self.text_color or default_text_color
def set_icon_color(self, *args) -> None:
"""
Set _theme_icon_color and _icon_color based on defaults and options.
"""
self._theme_icon_color = (
self.theme_icon_color or self._default_theme_icon_color
)
if self._default_icon_color == "PrimaryHue":
default_icon_color = text_colors[self.theme_cls.primary_palette][
self.theme_cls.primary_hue
]
elif self._default_icon_color == "Primary":
default_icon_color = self.theme_cls.primary_color
else:
default_icon_color = self.theme_cls.text_color
self._icon_color = self.icon_color or default_icon_color
def set_radius(self, *args) -> None:
"""
Set the radius, if we are a rounded button, based on the
current height.
"""
if self.rounded_button:
self._radius = self.height / 2
# Touch events that cause transparent buttons to fade to background
def on_touch_down(self, touch):
"""
Animates fade to background on press, for buttons with no
background color.
"""
if touch.is_mouse_scrolling:
return False
elif not self.collide_point(touch.x, touch.y):
return False
elif self in touch.ud:
return False
elif self.disabled:
return False
else:
if self._md_bg_color[3] == 0.0:
self._animation_fade_bg = Animation(
duration=0.5, _md_bg_color=[0.0, 0.0, 0.0, 0.1]
)
self._animation_fade_bg.start(self)
return super().on_touch_down(touch)
def on_touch_up(self, touch):
"""Animates return to original background on touch release."""
if not self.disabled and self._animation_fade_bg:
self._animation_fade_bg.stop_property(self, "_md_bg_color")
self._animation_fade_bg = None
md_bg_color = (
self.md_bg_color
or self._default_md_bg_color
or self.theme_cls.primary_color
)
Animation(duration=0.05, _md_bg_color=md_bg_color).start(self)
return super().on_touch_up(touch)
def on_disabled(self, instance_button, disabled_value: bool) -> None:
Clock.schedule_once(self.set_disabled_color)
class ButtonElevationBehaviour(CommonElevationBehavior):
"""
Implements elevation behavior as well as the recommended down/disabled
colors for raised buttons.
The minimum elevation for any raised button is `'1dp'`,
by default, set to `'2dp'`.
The `_elevation_raised` is automatically computed and is set to
`self.elevation + 6` each time `self.elevation` is updated.
"""
_elevation_raised = NumericProperty()
_anim_raised = ObjectProperty(None, allownone=True)
_default_elevation = 2
def __init__(self, **kwargs):
if self.elevation == 0:
self.elevation = self._default_elevation
super().__init__(**kwargs)
self.bind(_radius=self.setter("radius"))
self.on_elevation(self, self.elevation)
def on_elevation(self, instance_button, elevation_value: int) -> None:
super().on_elevation(instance_button, elevation_value)
self._elevation_raised = self.elevation + 6
self.on_disabled(self, self.disabled)
def on__elevation_raised(
self, instance_button, elevation_value: int
) -> None:
Animation.cancel_all(self, "_elevation")
self._anim_raised = Animation(_elevation=self._elevation_raised, d=0.15)
def on_disabled(self, instance_button, disabled_value: bool) -> None:
if self.disabled is True:
Animation.cancel_all(self, "_elevation")
super().on_disabled(instance_button, disabled_value)
def on_touch_down(self, touch):
if not self.disabled:
if touch.is_mouse_scrolling:
return False
if not self.collide_point(touch.x, touch.y):
return False
if self in touch.ud:
return False
if self._anim_raised:
self._anim_raised.start(self)
return super().on_touch_down(touch)
def on_touch_up(self, touch):
if not self.disabled:
if touch.grab_current is not self:
self.stop_elevation_anim()
return super().on_touch_up(touch)
self.stop_elevation_anim()
return super().on_touch_up(touch)
def stop_elevation_anim(self):
Animation.cancel_all(self, "_elevation")
self._elevation = self.elevation
class ButtonContentsText:
"""Contents for :class:`~BaseButton` class consisting of a single label."""
class ButtonContentsIcon:
"""
Contents for a round BaseButton consisting of an :class:`~MDIcon` class.
"""
_min_width = NumericProperty(0)
def __init__(self, **kwargs):
super().__init__(**kwargs)
if self.user_font_size:
self.icon_size = self.user_font_size
self.bind(user_font_size=self.setter("icon_size"))
def on_text_color(self, instance_button, color: list) -> None:
"""
Set icon_color equal to text_color.
For backwards compatibility - can use text_color instead
of icon_color.
"""
if color:
self.icon_color = color
class ButtonContentsIconText:
"""
Contents for :class:`~BaseButton` class consisting of a
:class:`~kivy.uix.boxlayout.BoxLayout` with an icon and a label.
"""
padding = VariableListProperty([dp(12), dp(8), dp(16), dp(8)])
"""
Padding between the widget box and its children, in pixels:
[padding_left, padding_top, padding_right, padding_bottom].
padding also accepts a two argument form [padding_horizontal,
padding_vertical] and a one argument form [padding].
.. versionadded:: 1.0.0
:attr:`padding` is a :class:`~kivy.properties.VariableListProperty`
and defaults to [12dp, 8dp, 16dp, 8dp].
"""
# Old MD Button classes
class OldButtonIconMixin:
"""Backwards-compatibility for icons."""
icon = StringProperty("android")
def on_icon_color(self, instance_button, color: list) -> None:
"""
If we are setting an icon color, set theme_icon_color to Custom.
For backwards compatibility (before theme_icon_color existed).
"""
if color and (self.theme_text_color == "Custom"):
self.theme_icon_color = "Custom"
class MDFlatButton(ButtonContentsText, BaseButton):
"""
A flat rectangular button with (by default) no border or background.
Text is the default text color.
"""
padding = VariableListProperty([dp(8), dp(8), dp(8), dp(8)])
"""
Padding between the widget box and its children, in pixels:
[padding_left, padding_top, padding_right, padding_bottom].
padding also accepts a two argument form [padding_horizontal,
padding_vertical] and a one argument form [padding].
.. versionadded:: 1.0.0
:attr:`padding` is a :class:`~kivy.properties.VariableListProperty`
and defaults to [8dp, 8dp, 8dp, 8dp].
"""
class MDRaisedButton(
FakeRectangularElevationBehavior,
ButtonElevationBehaviour,
ButtonContentsText,
BaseButton,
):
"""
A flat button with (by default) a primary color fill and matching
color text.
"""
# FIXME: Move the underlying attributes to the :class:`~BaseButton` class.
# This applies to all classes of buttons that have similar attributes.
_default_md_bg_color = None
_default_md_bg_color_disabled = None
_default_theme_text_color = "Custom"
_default_text_color = "PrimaryHue"
class MDRectangleFlatButton(ButtonContentsText, BaseButton):
"""
A flat button with (by default) a primary color border and primary
color text.
"""
_default_line_color = None
_default_line_color_disabled = None
_default_theme_text_color = "Custom"
_default_text_color = "Primary"
class MDRectangleFlatIconButton(
OldButtonIconMixin, ButtonContentsIconText, BaseButton
):
"""
A flat button with (by default) a primary color border, primary color text
and a primary color icon on the left.
"""
_default_line_color = None
_default_line_color_disabled = None
_default_theme_text_color = "Custom"
_default_theme_icon_color = "Custom"
_default_text_color = "Primary"
_default_icon_color = "Primary"
class MDRoundFlatButton(ButtonContentsText, BaseButton):
"""
A flat button with (by default) fully rounded corners, a primary
color border and primary color text.
"""
_default_line_color = None
_default_line_color_disabled = None
_default_theme_text_color = "Custom"
_default_text_color = "Primary"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rounded_button = True
class MDRoundFlatIconButton(
OldButtonIconMixin,
ButtonContentsIconText,
BaseButton,
):
"""
A flat button with (by default) rounded corners, a primary color border,
primary color text and a primary color icon on the left.
"""
_default_line_color = None
_default_line_color_disabled = None
_default_theme_text_color = "Custom"
_default_theme_icon_color = "Custom"
_default_text_color = "Primary"
_default_icon_color = "Primary"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rounded_button = True
class MDFillRoundFlatButton(ButtonContentsText, BaseButton):
"""
A flat button with (by default) rounded corners, a primary color fill
and primary color text.
"""
_default_md_bg_color = None
_default_md_bg_color_disabled = None
_default_theme_text_color = "Custom"
_default_text_color = "PrimaryHue"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rounded_button = True
class MDFillRoundFlatIconButton(
OldButtonIconMixin,
ButtonContentsIconText,
BaseButton,
):
"""
A flat button with (by default) rounded corners, a primary color fill,
primary color text and a primary color icon on the left.
"""
_default_md_bg_color = None
_default_md_bg_color_disabled = None
_default_theme_text_color = "Custom"
_default_theme_icon_color = "Custom"
_default_text_color = "PrimaryHue"
_default_icon_color = "PrimaryHue"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rounded_button = True
class MDIconButton(OldButtonIconMixin, ButtonContentsIcon, BaseButton):
"""A simple rounded icon button."""
icon = StringProperty("checkbox-blank-circle")
"""
Button icon.
:attr:`icon` is a :class:`~kivy.properties.StringProperty`
and defaults to `'checkbox-blank-circle'`.
"""
text_color = ColorProperty(None, deprecated=True)
"""
Button icon color in (r, g, b, a) format.
.. deprecated in 1.0.0::
Deprecated for :class:`~MDIconButton`. Use ``icon_color`` instead.
:attr:`text_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
theme_text_color = OptionProperty(
None, options=theme_text_color_options, deprecated=True
)
"""
Button icon type. Available options are: (`"Primary"`, `"Secondary"`,
`"Hint"`, `"Error"`, `"Custom"`, `"ContrastParentBackground"`).
.. deprecated in 1.0.0::
Deprecated for :class:`~MDIconButton`. Use ``theme_icon_color`` instead.
:attr:`theme_text_color` is an :class:`~kivy.properties.OptionProperty`
and defaults to `None` (set by button class).
"""
_min_width = NumericProperty(0)
_default_icon_pad = max(dp(48) - sp(24), 0)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.rounded_button = True
# FIXME: GraphicException: Invalid width value, must be > 0
self.line_width = 0.001
Clock.schedule_once(self.set_size)
def set_size(self, interval: Union[int, float]) -> None:
"""
Sets the icon width/height based on the current `icon_size`
attribute, or the default value if it is zero. The icon size
is set to `(48, 48)` for an icon with the default font_size 24sp.
"""
diameter = self._default_icon_pad + (self.icon_size or sp(24))
self.width = diameter
self.height = diameter
class MDFloatingActionButton(
OldButtonIconMixin,
RoundedRectangularElevationBehavior,
ButtonElevationBehaviour,
ButtonContentsIcon,
BaseButton,
):
"""
Implementation
`FAB <https://m3.material.io/components/floating-action-button/overview>`_
button.
"""
type = OptionProperty("standard", options=["small", "large", "standard"])
"""
Type of M3 button.
.. versionadded:: 1.0.0
Available options are: 'small', 'large', 'standard'.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-floating-action-button-types.png
:align: center
:attr:`type` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'standard'`.
"""
_default_md_bg_color = None
_default_md_bg_color_disabled = None
_default_theme_icon_color = "Custom"
_default_icon_color = "PrimaryHue"
def __init__(self, **kwargs):
super().__init__(**kwargs)
# FIXME: GraphicException: Invalid width value, must be > 0
self.line_width = 0.001
self.theme_cls.bind(material_style=self.set_size)
self.theme_cls.bind(material_style=self.set__radius)
Clock.schedule_once(self.set_size)
Clock.schedule_once(self.set__radius)
Clock.schedule_once(self.set_font_size)
def set_font_size(self, *args) -> None:
if self.theme_cls.material_style == "M3":
if self.type == "large":
self.icon_size = "36sp"
else:
self.icon_size = 0
def set__radius(self, *args) -> None:
if self.theme_cls.material_style == "M2":
self.rounded_button = True
else:
self.rounded_button = False
if self.type == "small":
self._radius = dp(12)
elif self.type == "standard":
self._radius = dp(16)
elif self.type == "large":
self._radius = dp(28)
def set_size(self, *args) -> None:
if self.theme_cls.material_style == "M2":
self.size = dp(56), dp(56)
else:
if self.type == "small":
self.size = dp(40), dp(40)
elif self.type == "standard":
self.size = dp(56), dp(56)
elif self.type == "large":
self.size = dp(96), dp(96)
def on_type(self, instance_md_floating_action_button, type: str) -> None:
self.set_size()
self.set_font_size()
class MDTextButton(ButtonBehavior, MDLabel):
color = ColorProperty(None)
"""
Button color in (r, g, b, a) format.
:attr:`color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
color_disabled = ColorProperty(None)
"""
Button color disabled in (r, g, b, a) format.
:attr:`color_disabled` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
_color = ColorProperty(None) # last current button text color
def animation_label(self) -> None:
def set_default_state_label(*args):
Animation(opacity=1, d=0.1, t="in_out_cubic").start(self)
anim = Animation(opacity=0.5, d=0.2, t="in_out_cubic")
anim.bind(on_complete=set_default_state_label)
anim.start(self)
def on_press(self, *args):
self.animation_label()
return super().on_press(*args)
def on_disabled(self, instance_button, disabled_value) -> None:
if disabled_value:
if not self.color_disabled:
self.color_disabled = self.theme_cls.disabled_hint_text_color
self._color = self.color
self.text_color = self.color_disabled
else:
self.text_color = self._color
# SpeedDial classes
class BaseFloatingRootButton(MDFloatingActionButton):
_angle = NumericProperty(0)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.elevation = 5
class BaseFloatingBottomButton(MDFloatingActionButton, MDTooltip):
_canvas_width = NumericProperty(0)
_padding_right = NumericProperty(0)
_bg_color = ColorProperty(None)
def set_size(self, interval: Union[int, float]) -> None:
self.width = "46dp"
self.height = "46dp"
# FIXME: Use :class:`~kivymd.uix.boxlayout.MDBoxLayout` instead
# :class:`~kivy.uix.boxlayout.BoxLayout`.
class BaseFloatingLabel(
ThemableBehavior, FakeRectangularElevationBehavior, BoxLayout
):
text = StringProperty()
text_color = ColorProperty(None)
bg_color = ColorProperty(None)
class MDFloatingBottomButton(BaseFloatingBottomButton):
pass
class MDFloatingRootButton(BaseFloatingRootButton):
pass
class MDFloatingLabel(BaseFloatingLabel):
pass
class MDFloatingActionButtonSpeedDial(ThemableBehavior, FloatLayout):
"""
:Events:
:attr:`on_open`
Called when a stack is opened.
:attr:`on_close`
Called when a stack is closed.
"""
icon = StringProperty("plus")
"""
Root button icon name.
:attr:`icon` is a :class:`~kivy.properties.StringProperty`
and defaults to `'plus'`.
"""
anchor = OptionProperty("right", option=["right"])
"""
Stack anchor. Available options are: `'right'`.
:attr:`anchor` is a :class:`~kivy.properties.OptionProperty`
and defaults to `'right'`.
"""
callback = ObjectProperty(lambda x: None)
"""
Custom callback.
.. code-block:: kv
MDFloatingActionButtonSpeedDial:
callback: app.callback
.. code-block:: python
def callback(self, instance):
print(instance.icon)
:attr:`callback` is a :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
label_text_color = ColorProperty([0, 0, 0, 1])
"""
Floating text color in (r, g, b, a) format.
:attr:`label_text_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `[0, 0, 0, 1]`.
"""
data = DictProperty()
"""
Must be a dictionary
.. code-block:: python
{
'name-icon': 'Text label',
...,
...,
}
"""
right_pad = BooleanProperty(True)
"""
If `True`, the button will increase on the right side by 2.5 pixels
if the :attr:`~hint_animation` parameter equal to `True`.
.. rubric:: False
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-right-pad.gif
:align: center
.. rubric:: True
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-right-pad-true.gif
:align: center
:attr:`right_pad` is a :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
root_button_anim = BooleanProperty(False)
"""
If ``True`` then the root button will rotate 45 degrees when the stack
is opened.
:attr:`root_button_anim` is a :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
opening_transition = StringProperty("out_cubic")
"""
The name of the stack opening animation type.
:attr:`opening_transition` is a :class:`~kivy.properties.StringProperty`
and defaults to `'out_cubic'`.
"""
closing_transition = StringProperty("out_cubic")
"""
The name of the stack closing animation type.
:attr:`closing_transition` is a :class:`~kivy.properties.StringProperty`
and defaults to `'out_cubic'`.
"""
opening_transition_button_rotation = StringProperty("out_cubic")
"""
The name of the animation type to rotate the root button when opening the
stack.
:attr:`opening_transition_button_rotation` is a :class:`~kivy.properties.StringProperty`
and defaults to `'out_cubic'`.
"""
closing_transition_button_rotation = StringProperty("out_cubic")
"""
The name of the animation type to rotate the root button when closing the
stack.
:attr:`closing_transition_button_rotation` is a :class:`~kivy.properties.StringProperty`
and defaults to `'out_cubic'`.
"""
opening_time = NumericProperty(0.5)
"""
Time required for the stack to go to: attr:`state` `'open'`.
:attr:`opening_time` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
closing_time = NumericProperty(0.2)
"""
Time required for the stack to go to: attr:`state` `'close'`.
:attr:`closing_time` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
opening_time_button_rotation = NumericProperty(0.2)
"""
Time required to rotate the root button 45 degrees during the stack
opening animation.
:attr:`opening_time_button_rotation` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
closing_time_button_rotation = NumericProperty(0.2)
"""
Time required to rotate the root button 0 degrees during the stack
closing animation.
:attr:`closing_time_button_rotation` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
state = OptionProperty("close", options=("close", "open"))
"""
Indicates whether the stack is closed or open.
Available options are: `'close'`, `'open'`.
:attr:`state` is a :class:`~kivy.properties.OptionProperty`
and defaults to `'close'`.
"""
bg_color_root_button = ColorProperty(None)
"""
Root button color in (r, g, b, a) format.
:attr:`bg_color_root_button` is a :class:`~kivy.properties.ColorProperty`
and defaults to `[]`.
"""
bg_color_stack_button = ColorProperty(None)
"""
The color of the buttons in the stack (r, g, b, a) format.
:attr:`bg_color_stack_button` is a :class:`~kivy.properties.ColorProperty`
and defaults to `[]`.
"""
color_icon_stack_button = ColorProperty(None)
"""
The color icon of the buttons in the stack (r, g, b, a) format.
:attr:`color_icon_stack_button` is a :class:`~kivy.properties.ColorProperty`
and defaults to `[]`.
"""
color_icon_root_button = ColorProperty(None)
"""
The color icon of the root button (r, g, b, a) format.
:attr:`color_icon_root_button` is a :class:`~kivy.properties.ColorProperty`
and defaults to `[]`.
"""
bg_hint_color = ColorProperty(None)
"""
Background color for the text of the buttons in the stack (r, g, b, a) format.
:attr:`bg_hint_color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
hint_animation = BooleanProperty(False)
"""
Whether to use button extension animation to display text labels.
:attr:`hint_animation` is a :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
_label_pos_y_set = False
_anim_buttons_data = {}
_anim_labels_data = {}
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.register_event_type("on_open")
self.register_event_type("on_close")
Window.bind(on_resize=self._update_pos_buttons)
def on_open(self, *args):
"""Called when a stack is opened."""
def on_close(self, *args):
"""Called when a stack is closed."""
def on_leave(self, instance_button: MDFloatingBottomButton) -> None:
"""Called when the mouse cursor goes outside the button of stack."""
if self.state == "open":
for widget in self.children:
if isinstance(widget, MDFloatingLabel) and self.hint_animation:
Animation.cancel_all(widget)
for item in self.data.items():
if widget.text in item:
Animation(
_canvas_width=0,
_padding_right=0,
d=self.opening_time,
t=self.opening_transition,
_elevation=0,
).start(instance_button)
Animation(
opacity=0, d=0.1, t=self.opening_transition
).start(widget)
def on_enter(self, instance_button: MDFloatingBottomButton) -> None:
"""Called when the mouse cursor is over a button from the stack."""
if self.state == "open":
for widget in self.children:
if isinstance(widget, MDFloatingLabel) and self.hint_animation:
widget._elevation = 0
Animation.cancel_all(widget)
for item in self.data.items():
if widget.text in item:
Animation(
_canvas_width=widget.width + dp(24),
_padding_right=dp(5) if self.right_pad else 0,
d=self.opening_time,
t=self.opening_transition,
).start(instance_button)
if (
instance_button.icon
== self.data[f"{widget.text}"]
):
Animation(
opacity=1,
d=self.opening_time,
t=self.opening_transition,
).start(widget)
else:
Animation(
opacity=0, d=0.1, t=self.opening_transition
).start(widget)
def on_data(self, instance_speed_dial, data: dict) -> None:
"""Creates a stack of buttons."""
# FIXME: Don't know how to fix AttributeError error:
# File "kivymd/uix/button.py", line 1597, in on_data
# self.add_widget(bottom_button)
# File "kivy/uix/floatlayout.py", line 140, in add_widget
# return super(FloatLayout, self).add_widget(widget, index, canvas)
# File "kivy/uix/layout.py", line 97, in add_widget
# return super(Layout, self).add_widget(widget, index, canvas)
# File "kivy/uix/widget.py", line 629, in add_widget
# canvas.add(widget.canvas)
# AttributeError: 'NoneType' object has no attribute 'add'
super().__init__()
self.clear_widgets()
self._anim_buttons_data = {}
self._anim_labels_data = {}
self._label_pos_y_set = False
# Bottom buttons.
for name, name_icon in data.items():
bottom_button = MDFloatingBottomButton(
icon=name_icon,
on_enter=self.on_enter,
on_leave=self.on_leave,
opacity=0,
)
bottom_button.bind(
on_release=lambda x=bottom_button: self.callback(x)
)
self.set_pos_bottom_buttons(bottom_button)
self.add_widget(bottom_button)
# Labels.
floating_text = name
if floating_text:
label = MDFloatingLabel(text=floating_text, opacity=0)
label.text_color = self.label_text_color
self.add_widget(label)
# Top root button.
root_button = MDFloatingRootButton(on_release=self.open_stack)
root_button.icon = self.icon
self.set_pos_root_button(root_button)
self.add_widget(root_button)
def on_icon(self, instance_speed_dial, name_icon: str) -> None:
self._get_count_widget(MDFloatingRootButton).icon = name_icon
def on_label_text_color(self, instance_speed_dial, color: list) -> None:
for widget in self.children:
if isinstance(widget, MDFloatingLabel):
widget.text_color = color
def on_color_icon_stack_button(
self, instance_speed_dial, color: list
) -> None:
for widget in self.children:
if isinstance(widget, MDFloatingBottomButton):
widget.text_color = color
def on_hint_animation(self, instance_speed_dial, value: bool) -> None:
for widget in self.children:
if isinstance(widget, MDFloatingLabel):
widget.bg_color = (0, 0, 0, 0)
def on_bg_hint_color(self, instance_speed_dial, color: list) -> None:
for widget in self.children:
if isinstance(widget, MDFloatingBottomButton):
widget._bg_color = color
def on_color_icon_root_button(
self, instance_speed_dial, color: list
) -> None:
self._get_count_widget(MDFloatingRootButton).text_color = color
def on_bg_color_stack_button(
self, instance_speed_dial, color: list
) -> None:
for widget in self.children:
if isinstance(widget, MDFloatingBottomButton):
widget.md_bg_color = color
def on_bg_color_root_button(self, instance_speed_dial, color: list) -> None:
self._get_count_widget(MDFloatingRootButton).md_bg_color = color
def set_pos_labels(self, instance_floating_label: MDFloatingLabel) -> None:
"""
Sets the position of the floating labels.
Called when the application's root window is resized.
"""
if self.anchor == "right":
instance_floating_label.x = (
Window.width - instance_floating_label.width - dp(86)
)
def set_pos_root_button(
self, instance_floating_root_button: MDFloatingRootButton
) -> None:
"""
Sets the position of the root button.
Called when the application's root window is resized.
"""
if self.anchor == "right":
instance_floating_root_button.y = dp(20)
instance_floating_root_button.x = Window.width - (dp(56) + dp(20))
def set_pos_bottom_buttons(
self, instance_floating_bottom_button: MDFloatingBottomButton
) -> None:
"""
Sets the position of the bottom buttons in a stack.
Called when the application's root window is resized.
"""
if self.anchor == "right":
if self.state != "open":
instance_floating_bottom_button.y = (
instance_floating_bottom_button.height / 2
)
instance_floating_bottom_button.x = Window.width - (
instance_floating_bottom_button.height
+ instance_floating_bottom_button.width / 2
)
def open_stack(
self, instance_floating_root_button: MDFloatingRootButton
) -> None:
"""Opens a button stack."""
for widget in self.children:
if isinstance(widget, MDFloatingLabel):
Animation.cancel_all(widget)
if self.state != "open":
y = 0
label_position = dp(56)
anim_buttons_data = {}
anim_labels_data = {}
for widget in self.children:
if isinstance(widget, MDFloatingBottomButton):
# Sets new button positions.
y += dp(56)
widget.y = widget.y * 2 + y
if not self._anim_buttons_data:
anim_buttons_data[widget] = Animation(
opacity=1,
d=self.opening_time,
t=self.opening_transition,
)
elif isinstance(widget, MDFloatingLabel):
# Sets new labels positions.
label_position += dp(56)
# Sets the position of signatures only once.
if not self._label_pos_y_set:
widget.y = widget.y * 2 + label_position
widget.x = Window.width - widget.width - dp(86)
if not self._anim_labels_data:
anim_labels_data[widget] = Animation(
opacity=1, d=self.opening_time
)
elif (
isinstance(widget, MDFloatingRootButton)
and self.root_button_anim
):
# Rotates the root button 45 degrees.
Animation(
_angle=-45,
d=self.opening_time_button_rotation,
t=self.opening_transition_button_rotation,
).start(widget)
if anim_buttons_data:
self._anim_buttons_data = anim_buttons_data
if anim_labels_data and not self.hint_animation:
self._anim_labels_data = anim_labels_data
self.state = "open"
self.dispatch("on_open")
self.do_animation_open_stack(self._anim_buttons_data)
self.do_animation_open_stack(self._anim_labels_data)
if not self._label_pos_y_set:
self._label_pos_y_set = True
else:
self.close_stack()
def do_animation_open_stack(self, anim_data: dict) -> None:
"""
:param anim_data:
{
<kivymd.uix.button.MDFloatingBottomButton object>:
<kivy.animation.Animation>,
<kivymd.uix.button.MDFloatingBottomButton object>:
<kivy.animation.Animation object>,
...,
}
"""
def on_progress(animation, widget, value):
if value >= 0.1:
animation_open_stack()
def animation_open_stack(*args):
try:
widget = next(widgets_list)
animation = anim_data[widget]
animation.bind(on_progress=on_progress)
animation.start(widget)
except StopIteration:
pass
widgets_list = iter(list(anim_data.keys()))
animation_open_stack()
def close_stack(self):
"""Closes the button stack."""
for widget in self.children:
if isinstance(widget, MDFloatingBottomButton):
Animation(
y=widget.height / 2,
d=self.closing_time,
t=self.closing_transition,
opacity=0,
).start(widget)
elif isinstance(widget, MDFloatingLabel):
Animation(opacity=0, d=0.1).start(widget)
elif (
isinstance(widget, MDFloatingRootButton)
and self.root_button_anim
):
Animation(
_angle=0,
d=self.closing_time_button_rotation,
t=self.closing_transition_button_rotation,
).start(widget)
self.state = "close"
self.dispatch("on_close")
def _update_pos_buttons(self, instance, width, height):
# Updates button positions when resizing screen.
for widget in self.children:
if isinstance(widget, MDFloatingBottomButton):
self.set_pos_bottom_buttons(widget)
elif isinstance(widget, MDFloatingRootButton):
self.set_pos_root_button(widget)
elif isinstance(widget, MDFloatingLabel):
self.set_pos_labels(widget)
def _get_count_widget(self, instance):
widget = None
for widget in self.children:
if isinstance(widget, instance):
break
return widget