""" 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 ------------ .. 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 `_ """ __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 `_ 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: { : , : , ..., } """ 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