""" Components/Button ================= .. seealso:: `Material Design spec, Buttons `_ `Material Design spec, 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 ------------ .. tabs:: .. tab:: Declarative KV style .. 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): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" return Builder.load_string(KV) Example().run() .. tab:: Declarative python style .. code-block:: python from kivymd.app import MDApp from kivymd.uix.button import MDIconButton from kivymd.uix.screen import MDScreen class Example(MDApp): def build(self): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" return ( MDScreen( MDIconButton( icon="language-python", pos_hint={"center_x": 0.5, "center_y": 0.5}, ) ) ) Example().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-icon-button.png :align: center 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: "kivymd/images/logo/kivymd-icon-256.png" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-icon-custom-button.png :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.png :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``. .. 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 Example(MDApp): def build(self): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" 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"], ) ) Example().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-floating-action-button-m3.png :align: center .. MDFlatButton: MDFlatButton ------------ 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: "orange" .. 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 -------------- 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: "red" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-raised-button.png :align: center .. MDRectangleFlatButton: MDRectangleFlatButton --------------------- .. code-block:: kv MDRectangleFlatButton: text: "MDRectangleFlatButton" theme_text_color: "Custom" text_color: "white" line_color: "red" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-rectangle-flat-button-md-bg-color.png :align: center .. MDRectangleFlatIconButton: MDRectangleFlatIconButton ------------------------- 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: "white" line_color: "red" theme_icon_color: "Custom" icon_color: "orange" .. 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): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" return ( MDScreen( MDRectangleFlatIconButton( text="MDRectangleFlatIconButton", icon="language-python", line_color=(0, 0, 0, 0), pos_hint={"center_x": .5, "center_y": .5}, ) ) ) 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} .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-rectangle-flat-icon-button-without-border.png :align: center .. MDRoundFlatButton: MDRoundFlatButton ----------------- .. code-block:: kv MDRoundFlatButton: text: "MDRoundFlatButton" text_color: "white" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-round-flat-button-text-color.png :align: center .. MDRoundFlatIconButton: MDRoundFlatIconButton --------------------- 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: text: "MDRoundFlatIconButton" icon: "android" text_color: "white" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-round-flat-icon-button.png :align: center .. 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 ------------ .. code-block:: kv MDTextButton: text: "MDTextButton" custom_color: "white" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/md-text-button.png :align: center .. 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): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" 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: .. tabs:: .. tab:: Imperative python style .. 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): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" 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() .. tab:: Declarative python style .. code-block:: python from kivymd.uix.screen import MDScreen from kivymd.app import MDApp from kivymd.uix.button import MDFloatingActionButtonSpeedDial class Example(MDApp): def build(self): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" return ( MDScreen( MDFloatingActionButtonSpeedDial( data={ 'Python': 'language-python', 'PHP': 'language-php', 'C++': 'language-cpp', }, root_button_anim=True, ) ) ) 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: hint_animation: True bg_hint_color: app.theme_cls.primary_dark .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-hint-color.png :align: center Binds to individual buttons --------------------------- .. tabs:: .. tab:: Declarative KV style .. code-block:: python from kivy.lang import Builder from kivy.properties import DictProperty from kivymd.app import MDApp KV = ''' MDScreen: MDFloatingActionButtonSpeedDial: id: speed_dial data: app.data root_button_anim: True hint_animation: True ''' class Example(MDApp): data = DictProperty() def build(self): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" self.data = { 'Python': 'language-python', 'JS': [ 'language-javascript', "on_press", lambda x: print("pressed JS"), "on_release", lambda x: print( "stack_buttons", self.root.ids.speed_dial.stack_buttons ) ], 'PHP': [ 'language-php', "on_press", lambda x: print("pressed PHP"), "on_release", self.callback ], 'C++': [ 'language-cpp', "on_press", lambda x: print("pressed C++"), "on_release", lambda x: self.callback() ], } return Builder.load_string(KV) def callback(self, *args): print(args) Example().run() .. tab:: Declarative python style .. code-block:: python from kivymd.app import MDApp from kivymd.uix.button import MDFloatingActionButtonSpeedDial from kivymd.uix.screen import MDScreen class Example(MDApp): def build(self): self.theme_cls.theme_style = "Dark" self.theme_cls.primary_palette = "Orange" return ( MDScreen( MDFloatingActionButtonSpeedDial( id="speed_dial", hint_animation=True, root_button_anim=True, ) ) ) def on_start(self): data = { "Python": "language-python", "JS": [ "language-javascript", "on_press", lambda x: print("pressed JS"), "on_release", lambda x: print( "stack_buttons", self.root.ids.speed_dial.stack_buttons ) ], "PHP": [ "language-php", "on_press", lambda x: print("pressed PHP"), "on_release", self.callback ], "C++": [ "language-cpp", "on_press", lambda x: print("pressed C++"), "on_release", lambda x: self.callback() ], } self.root.ids.speed_dial.data = data def callback(self, *args): print(args) Example().run() """ from __future__ import annotations __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.floatlayout import FloatLayout from kivy.weakproxy import WeakProxy from kivymd import uix_path from kivymd.color_definitions import text_colors from kivymd.font_definitions import theme_font_styles from kivymd.material_resources import ( FLOATING_ACTION_BUTTON_M2_ELEVATION, FLOATING_ACTION_BUTTON_M2_OFFSET, FLOATING_ACTION_BUTTON_M3_ELEVATION, FLOATING_ACTION_BUTTON_M3_OFFSET, FLOATING_ACTION_BUTTON_M3_SOFTNESS, RAISED_BUTTON_OFFSET, RAISED_BUTTON_SOFTNESS, ) from kivymd.theming import ThemableBehavior from kivymd.uix.behaviors import ( CommonElevationBehavior, DeclarativeBehavior, RectangularRippleBehavior, RotateBehavior, ) 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( DeclarativeBehavior, RectangularRippleBehavior, ThemableBehavior, ButtonBehavior, AnchorLayout, ): """ Base class for all buttons. For more information, see in the :class:`~kivymd.uix.behaviors.DeclarativeBehavior` and :class:`~kivymd.uix.behaviors.RectangularRippleBehavior` and :class:`~kivymd.theming.ThemableBehavior` and :class:`~kivy.uix.behaviors.ButtonBehavior` and :class:`~kivy.uix.anchorlayout.AnchorLayout` classes documentation. """ 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) or string 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) or string 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`. """ 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 in (r, g, b, a) or string format for button border. :attr:`line_color` is a :class:`~kivy.properties.ColorProperty` and defaults to `None`. """ line_color_disabled = ColorProperty(None) """ Disabled line color in (r, g, b, a) or string format 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 in (r, g, b, a) or string format. :attr:`md_bg_color` is a :class:`~kivy.properties.ColorProperty` and defaults to `None`. """ md_bg_color_disabled = ColorProperty(None) """ The background color in (r, g, b, a) or string format 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 (r, g, b, a) or string 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([0.0, 0.0, 0.0, 0.0]) _md_bg_color = ColorProperty([0.0, 0.0, 0.0, 0.0]) _md_bg_color_disabled = ColorProperty([0.0, 0.0, 0.0, 0.0]) _line_color = ColorProperty([0.0, 0.0, 0.0, 0.0]) _line_color_disabled = ColorProperty([0.0, 0.0, 0.0, 0.0]) _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, *args, **kwargs): super().__init__(*args, **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 _md_bg_color = ( self.md_bg_color or self._default_md_bg_color or self.theme_cls.primary_color ) # Set disabled color _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 _line_color = ( self.line_color or self._default_line_color or self.theme_cls.primary_color ) # Set disabled line color _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 ) if self.theme_cls.theme_style_switch_animation: Animation( _md_bg_color=_md_bg_color, _md_bg_color_disabled=_md_bg_color_disabled, _line_color=_line_color, _line_color_disabled=_line_color_disabled, d=self.theme_cls.theme_style_switch_animation_duration, t="linear", ).start(self) else: self._md_bg_color = _md_bg_color self._md_bg_color_disabled = _md_bg_color_disabled self._line_color = _line_color self._line_color_disabled = _line_color_disabled 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 not self.disabled else "Custom" ) 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: if hasattr(super(), "on_disabled"): if self.disabled is True: Animation.cancel_all(self, "elevation") super().on_disabled(instance_button, disabled_value) 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): super().__init__(**kwargs) if self.elevation == 0: self.elevation = self._default_elevation if hasattr(self, "radius"): self.bind(_radius=self.setter("radius")) Clock.schedule_once(self.create_anim_raised) self.on_disabled(self, self.disabled) def create_anim_raised(self, *args) -> None: if self.elevation: self._elevation_raised = self.elevation self._anim_raised = Animation(elevation=self.elevation + 1, d=0.15) 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 and self.elevation: self._anim_raised.start(self) return super().on_touch_down(touch) def on_touch_up(self, touch): if not self.disabled: if self in touch.ud: self.stop_elevation_anim() return super().on_touch_up(touch) return super().on_touch_up(touch) def stop_elevation_anim(self): Animation.cancel_all(self, "elevation") if self._anim_raised and self.elevation: self.elevation = self._elevation_raised 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 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(BaseButton, ButtonContentsText): """ A flat rectangular button with (by default) no border or background. Text is the default text color. For more information, see in the :class:`~BaseButton` and :class:`~ButtonContentsText` classes documentation. """ 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(BaseButton, ButtonElevationBehaviour, ButtonContentsText): """ A flat button with (by default) a primary color fill and matching color text. For more information, see in the :class:`~BaseButton` and :class:`~ButtonElevationBehaviour` and :class:`~ButtonContentsText` classes documentation. """ # 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" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.shadow_softness = RAISED_BUTTON_SOFTNESS self.shadow_offset = RAISED_BUTTON_OFFSET # self.shadow_radius = self._radius * 2 class MDRectangleFlatButton(BaseButton, ButtonContentsText): """ A flat button with (by default) a primary color border and primary color text. For more information, see in the :class:`~BaseButton` and :class:`~ButtonContentsText` classes documentation. """ _default_line_color = None _default_line_color_disabled = None _default_theme_text_color = "Custom" _default_text_color = "Primary" class MDRectangleFlatIconButton( BaseButton, OldButtonIconMixin, ButtonContentsIconText ): """ A flat button with (by default) a primary color border, primary color text and a primary color icon on the left. For more information, see in the :class:`~BaseButton` and :class:`~OldButtonIconMixin` and :class:`~ButtonContentsIconText` classes documentation. """ _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(BaseButton, ButtonContentsText): """ A flat button with (by default) fully rounded corners, a primary color border and primary color text. For more information, see in the :class:`~BaseButton` and :class:`~ButtonContentsText` classes documentation. """ _default_line_color = None _default_line_color_disabled = None _default_theme_text_color = "Custom" _default_text_color = "Primary" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.rounded_button = True class MDRoundFlatIconButton( BaseButton, OldButtonIconMixin, ButtonContentsIconText ): """ A flat button with (by default) rounded corners, a primary color border, primary color text and a primary color icon on the left. For more information, see in the :class:`~BaseButton` and :class:`~OldButtonIconMixin` and :class:`~ButtonContentsIconText` classes documentation. """ _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, *args, **kwargs): super().__init__(*args, **kwargs) self.rounded_button = True class MDFillRoundFlatButton(BaseButton, ButtonContentsText): """ A flat button with (by default) rounded corners, a primary color fill and primary color text. For more information, see in the :class:`~BaseButton` and :class:`~ButtonContentsText` classes documentation. """ _default_md_bg_color = None _default_md_bg_color_disabled = None _default_theme_text_color = "Custom" _default_text_color = "PrimaryHue" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.rounded_button = True class MDFillRoundFlatIconButton( BaseButton, OldButtonIconMixin, ButtonContentsIconText ): """ A flat button with (by default) rounded corners, a primary color fill, primary color text and a primary color icon on the left. For more information, see in the :class:`~BaseButton` and :class:`~OldButtonIconMixin` and :class:`~ButtonContentsIconText` classes documentation. """ _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, *args, **kwargs): super().__init__(*args, **kwargs) self.rounded_button = True class MDIconButton(BaseButton, OldButtonIconMixin, ButtonContentsIcon): """ A simple rounded icon button. For more information, see in the :class:`~BaseButton` and :class:`~OldButtonIconMixin` and :class:`~ButtonContentsIcon` classes documentation. """ icon = StringProperty("checkbox-blank-circle") """ Button icon. :attr:`icon` is a :class:`~kivy.properties.StringProperty` and defaults to `'checkbox-blank-circle'`. """ _min_width = NumericProperty(0) _default_icon_pad = max(dp(48) - sp(24), 0) def __init__(self, *args, **kwargs): super().__init__(*args, **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( BaseButton, OldButtonIconMixin, ButtonElevationBehaviour, ButtonContentsIcon ): """ Implementation `FAB `_ button. For more information, see in the :class:`~BaseButton` and :class:`~OldButtonIconMixin` and :class:`~ButtonElevationBehaviour` and :class:`~ButtonContentsIcon` classes documentation. """ 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, *args, **kwargs): super().__init__(*args, **kwargs) # FIXME: GraphicException: Invalid width value, must be > 0 self.line_width = 0.001 self.theme_cls.bind(material_style=self.set_size_and_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.shadow_radius = self.height / 2 self.elevation = FLOATING_ACTION_BUTTON_M2_ELEVATION self.shadow_offset = FLOATING_ACTION_BUTTON_M2_OFFSET self.rounded_button = True else: self.shadow_softness = FLOATING_ACTION_BUTTON_M3_SOFTNESS self.shadow_offset = FLOATING_ACTION_BUTTON_M3_OFFSET self.elevation = FLOATING_ACTION_BUTTON_M3_ELEVATION 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) self.shadow_radius = self._radius def set_size_and_radius(self, *args) -> None: self.set_size(args) self.set__radius(args) 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): """ Text button class. For more information, see in the :class:`~kivy.uix.behaviors.ButtonBehavior` and :class:`~kivymd.uix.label.MDLabel` classes documentation. """ color = ColorProperty(None) """ Button color in (r, g, b, a) or string 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) or string 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 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" class MDFloatingBottomButton(BaseFloatingBottomButton): _bg_color = ColorProperty(None) class MDFloatingRootButton(RotateBehavior, MDFloatingActionButton): rotate_value_angle = NumericProperty(0) class MDFloatingLabel(MDLabel): bg_color = ColorProperty([0, 0, 0, 0]) class MDFloatingActionButtonSpeedDial( DeclarativeBehavior, ThemableBehavior, FloatLayout ): """ For more information, see in the :class:`~kivy.uix.floatlayout.FloatLayout` class documentation. For more information, see in the :class:`~kivymd.uix.behaviors.DeclarativeBehavior` and :class:`~kivymd.theming.ThemableBehavior` and :class:`~kivy.uix.floatlayout.FloatLayout` lasses documentation. :Events: :attr:`on_open` Called when a stack is opened. :attr:`on_close` Called when a stack is closed. :attr:`on_press_stack_button` Called at the on_press event for the stack button. :attr:`on_release_stack_button` Called at the on_press event for the stack button. """ icon = StringProperty("plus") """ Root button icon name. .. code-block:: kv MDFloatingActionButtonSpeedDial: icon: "pencil" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-icon.png :align: center :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'`. """ label_text_color = ColorProperty(None) """ Color of floating text labels in (r, g, b, a) or string format. .. code-block:: kv MDFloatingActionButtonSpeedDial: label_text_color: "orange" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-label-text-color.png :align: center :attr:`label_text_color` is a :class:`~kivy.properties.ColorProperty` and defaults to `None`. """ label_bg_color = ColorProperty([0, 0, 0, 0]) """ Background color of floating text labels in (r, g, b, a) or string format. .. code-block:: kv MDFloatingActionButtonSpeedDial: label_text_color: "black" label_bg_color: "orange" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-label-bg-color.png :align: center :attr:`label_bg_color` is a :class:`~kivy.properties.ColorProperty` and defaults to `[0, 0, 0, 0]`. """ label_radius = VariableListProperty([0], length=4) """ The radius of the background of floating text labels. .. code-block:: kv MDFloatingActionButtonSpeedDial: label_text_color: "black" label_bg_color: "orange" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-label-radius.png :align: center :attr:`label_radius` is a :class:`~kivy.properties.ColorProperty` and defaults to `[0, 0, 0, 0]`. """ data = DictProperty() """ Must be a dictionary. .. code-block:: python { 'name-icon': 'Text label', ..., ..., } """ right_pad = BooleanProperty(False) """ If `True`, the background for the floating text label will increase by the number of pixels specified in the :attr:`~right_pad_value` parameter. Works only if the :attr:`~hint_animation` parameter is set to `True`. .. rubric:: False .. code-block:: kv MDFloatingActionButtonSpeedDial: hint_animation: True right_pad: False .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-right-pad.gif :align: center .. rubric:: True .. code-block:: kv MDFloatingActionButtonSpeedDial: hint_animation: True right_pad: True right_pad_value: "10dp" .. 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`. """ right_pad_value = NumericProperty(0) """ See :attr:`~right_pad` parameter for more information. :attr:`right_pad_value` is a :class:`~kivy.properties.NumericProperty` and defaults to `0`. """ 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) """ Background color of root button in (r, g, b, a) or string format. .. code-block:: kv MDFloatingActionButtonSpeedDial: bg_color_root_button: "red" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-bg-color-root-button.png :align: center :attr:`bg_color_root_button` is a :class:`~kivy.properties.ColorProperty` and defaults to `None`. """ bg_color_stack_button = ColorProperty(None) """ Background color of the stack buttons in (r, g, b, a) or string format. .. code-block:: kv MDFloatingActionButtonSpeedDial: bg_color_root_button: "red" bg_color_stack_button: "red" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-bg-color-stack-button.png :align: center :attr:`bg_color_stack_button` is a :class:`~kivy.properties.ColorProperty` and defaults to `None`. """ color_icon_stack_button = ColorProperty(None) """ The color icon of the stack buttons in (r, g, b, a) or string format. .. code-block:: kv MDFloatingActionButtonSpeedDial: bg_color_root_button: "red" bg_color_stack_button: "red" color_icon_stack_button: "white" .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-color-icon-stack-button.png :align: center :attr:`color_icon_stack_button` is a :class:`~kivy.properties.ColorProperty` and defaults to `None`. """ color_icon_root_button = ColorProperty(None) """ The color icon of the root button in (r, g, b, a) or string format. .. code-block:: kv MDFloatingActionButtonSpeedDial: bg_color_root_button: "red" bg_color_stack_button: "red" color_icon_stack_button: "white" color_icon_root_button: self.color_icon_stack_button .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-color-icon-root-button.png :align: center :attr:`color_icon_root_button` is a :class:`~kivy.properties.ColorProperty` and defaults to `None`. """ bg_hint_color = ColorProperty(None) """ Background color for the floating text of the buttons in (r, g, b, a) or string format. .. code-block:: kv MDFloatingActionButtonSpeedDial: bg_hint_color: "red" hint_animation: True .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDFloatingActionButtonSpeedDial-bg-hint-color.png :align: center :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 floating text. :attr:`hint_animation` is a :class:`~kivy.properties.BooleanProperty` and defaults to `False`. """ stack_buttons = DictProperty() _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") self.register_event_type("on_press_stack_button") self.register_event_type("on_release_stack_button") 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: Animation.cancel_all(widget) for item in self.data.items(): if widget.text in item: Animation( _canvas_width=widget.width + dp(24), _padding_right=self.right_pad_value 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}"] or instance_button.icon == self.data[f"{widget.text}"][0] ): 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.""" def on_data(*args): # Bottom buttons. for name, parameters in data.items(): name_icon = ( parameters if (type(parameters) is str) else parameters[0] ) bottom_button = MDFloatingBottomButton( icon=name_icon, on_enter=self.on_enter, on_leave=self.on_leave, opacity=0, ) bottom_button.bind( on_press=lambda x: self.dispatch("on_press_stack_button"), on_release=lambda x: self.dispatch( "on_release_stack_button" ), ) if "on_press" in parameters: callback = parameters[parameters.index("on_press") + 1] bottom_button.bind(on_press=callback) if "on_release" in parameters: callback = parameters[parameters.index("on_release") + 1] bottom_button.bind(on_release=callback) self.set_pos_bottom_buttons(bottom_button) self.add_widget(bottom_button) self.stack_buttons[name] = WeakProxy(bottom_button) # Labels. floating_text = name if floating_text: label = MDFloatingLabel(text=floating_text, opacity=0) label.bg_color = self.label_bg_color label.radius = self.label_radius label.text_color = ( self.label_text_color if self.label_text_color else self.theme_cls.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) self.clear_widgets() self.stack_buttons = {} self._anim_buttons_data = {} self._anim_labels_data = {} self._label_pos_y_set = False Clock.schedule_once(on_data) def on_icon(self, instance_speed_dial, name_icon: str) -> None: self._set_button_property(MDFloatingRootButton, "icon", name_icon) def on_label_text_color( self, instance_speed_dial, color: list | str ) -> 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: self._set_button_property(MDFloatingBottomButton, "icon_color", color) def on_hint_animation(self, instance_speed_dial, value: bool) -> None: for widget in self.children: if isinstance(widget, MDFloatingLabel): widget.md_bg_color = (0, 0, 0, 0) def on_bg_hint_color(self, instance_speed_dial, color: list) -> None: setattr(MDFloatingBottomButton, "_bg_color", color) def on_color_icon_root_button( self, instance_speed_dial, color: list ) -> None: self._set_button_property(MDFloatingRootButton, "icon_color", color) def on_bg_color_stack_button( self, instance_speed_dial, color: list ) -> None: self._set_button_property(MDFloatingBottomButton, "md_bg_color", color) def on_bg_color_root_button(self, instance_speed_dial, color: list) -> None: self._set_button_property(MDFloatingRootButton, "md_bg_color", color) def on_press_stack_button(self, *args) -> None: """ Called at the on_press event for the stack button. .. code-block:: kv MDFloatingActionButtonSpeedDial: on_press_stack_button: print(*args) .. versionadded:: 1.1.0 """ def on_release_stack_button(self, *args) -> None: """ Called at the on_release event for the stack button. .. code-block:: kv MDFloatingActionButtonSpeedDial: on_release_stack_button: print(*args) .. versionadded:: 1.1.0 """ 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. """ def set_pos_root_button(*args): if self.anchor == "right": instance_floating_root_button.y = dp(20) instance_floating_root_button.x = self.parent.width - ( dp(56) + dp(20) ) Clock.schedule_once(set_pos_root_button) 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(54) 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( rotate_value_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: { : , : , ..., } """ 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): if widget.opacity > 0: Animation(opacity=0, d=0.1).start(widget) elif ( isinstance(widget, MDFloatingRootButton) and self.root_button_anim ): Animation( rotate_value_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 _set_button_property( self, instance, property_name: str, property_value: str | list ): def set_count_widget(*args): if self.children: for widget in self.children: if isinstance(widget, instance): setattr(instance, property_name, property_value) Clock.unschedule(set_count_widget) break Clock.schedule_interval(set_count_widget, 0)