"""
Components/Appbar
=================

.. seealso::

    `Material Design spec, App bars: top <https://m3.material.io/components/top-app-bar/overview>`_

    `Material Design spec, App bars: bottom <https://m3.material.io/components/bottom-app-bar/overview>`_

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/app-bar-top.png
    :align: center

`KivyMD` provides the following bar positions for use:

- TopAppBar_
- BottomAppBar_

.. TopAppBar_:

TopAppBar
---------

- Contains a title and actions related to the current screen
- Four types: center-aligned, small, medium, and large
- On scroll, apply a container fill color to separate app bar from body content
- Top app bars have the same width as the device window

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/top-appbar-available-types.png
    :align: center

1. Center-aligned
2. Small
3. Medium
4. Large

.. note:: KivyMD does not provide a `Center-aligned` type panel. But you can
    easily create this pit panel yourself (read the documentation below).

Usage
-----

.. code-block:: kv

    MDTopAppBar:
        type: "small"

        MDTopAppBarLeadingButtonContainer:

            MDActionTopAppBarButton:
                icon: "menu"

        MDTopAppBarTitle:
            text: "AppBar Center-aligned"
            pos_hint: {"center_x": .5}

        MDTopAppBarTrailingButtonContainer:

            MDActionTopAppBarButton:
                icon: "account-circle-outline"

Anatomy
-------

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/top-appbar-anatomy.png
    :align: center

Configurations
==============

1. Center-aligned
-----------------

.. code-block:: kv

    MDScreen:
        md_bg_color: self.theme_cls.secondaryContainerColor

        MDTopAppBar:
            type: "small"
            size_hint_x: .8
            pos_hint: {"center_x": .5, "center_y": .5}

            MDTopAppBarLeadingButtonContainer:

                MDActionTopAppBarButton:
                    icon: "menu"

            MDTopAppBarTitle:
                text: "AppBar small"
                pos_hint: {"center_x": .5}

            MDTopAppBarTrailingButtonContainer:

                MDActionTopAppBarButton:
                    icon: "account-circle-outline"

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/top-appbar-center-aligned.png
    :align: center

2. Small
--------

.. code-block:: kv

    MDScreen:
        md_bg_color: self.theme_cls.secondaryContainerColor

        MDTopAppBar:
            type: "small"
            size_hint_x: .8
            pos_hint: {"center_x": .5, "center_y": .5}

            MDTopAppBarLeadingButtonContainer:

                MDActionTopAppBarButton:
                    icon: "arrow-left"

            MDTopAppBarTitle:
                text: "AppBar small"

            MDTopAppBarTrailingButtonContainer:

                MDActionTopAppBarButton:
                    icon: "attachment"

                MDActionTopAppBarButton:
                    icon: "calendar"

                MDActionTopAppBarButton:
                    icon: "dots-vertical"

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/top-appbar-small.png
    :align: center

3. Medium
---------

.. code-block:: kv

    MDTopAppBar:
        type: "medium"

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/top-appbar-medium.png
    :align: center

4. Large
--------

.. code-block:: kv

    MDTopAppBar:
        type: "large"

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/top-appbar-large.png
    :align: center

.. BottomAppBar:

BottomAppBar
------------

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/app-bar-bottom-m3.png
    :align: center

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp

    KV = '''
    MDScreen:
        md_bg_color: self.theme_cls.backgroundColor

        MDBottomAppBar:

            MDFabBottomAppBarButton:
                icon: "plus"
    '''


    class Example(MDApp):
        def build(self):
            self.theme_cls.theme_style = "Dark"
            return Builder.load_string(KV)


    Example().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-app-bar-m3-style-1.png
    :align: center

Add action items
----------------

.. code-block:: kv

    #:import MDActionBottomAppBarButton kivymd.uix.appbar.MDActionBottomAppBarButton


    MDScreen:

        MDBottomAppBar:
            action_items:
                [
                MDActionBottomAppBarButton(icon="gmail"),
                MDActionBottomAppBarButton(icon="label-outline"),
                MDActionBottomAppBarButton(icon="bookmark"),
                ]

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-app-bar-m3-style-2.png
    :align: center

Change action items
-------------------

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.appbar import MDActionBottomAppBarButton

    KV = '''
    #:import MDActionBottomAppBarButton kivymd.uix.appbar.MDActionBottomAppBarButton


    MDScreen:
        md_bg_color: self.theme_cls.backgroundColor

        MDBottomAppBar:
            id: bottom_appbar
            action_items:
                [
                MDActionBottomAppBarButton(icon="gmail"),
                MDActionBottomAppBarButton(icon="bookmark"),
                ]

            MDFabBottomAppBarButton:
                icon: "plus"
                on_release: app.change_actions_items()
    '''


    class Example(MDApp):
        def change_actions_items(self):
            self.root.ids.bottom_appbar.action_items = [
                MDActionBottomAppBarButton(icon="magnify"),
                MDActionBottomAppBarButton(icon="trash-can-outline"),
                MDActionBottomAppBarButton(icon="download-box-outline"),
            ]

        def build(self):
            self.theme_cls.theme_style = "Dark"
            return Builder.load_string(KV)


    Example().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-app-bar-m3-style-3.gif
    :align: center

A practical example
-------------------

.. code-block:: python

    import asynckivy

    from kivy.clock import Clock
    from kivy.lang import Builder
    from kivy.properties import StringProperty, BooleanProperty, ObjectProperty
    from kivy.uix.behaviors import FocusBehavior
    from kivy.uix.recycleboxlayout import RecycleBoxLayout
    from kivy.uix.recycleview.layout import LayoutSelectionBehavior
    from kivy.uix.recycleview.views import RecycleDataViewBehavior

    from kivymd.uix.appbar import MDActionBottomAppBarButton
    from kivymd.uix.boxlayout import MDBoxLayout
    from kivymd.app import MDApp

    from faker import Faker  # pip install Faker

    KV = '''
    #:import MDFabBottomAppBarButton kivymd.uix.appbar.MDFabBottomAppBarButton


    <UserCard>
        orientation: "vertical"
        adaptive_height: True
        md_bg_color: "#373A22" if self.selected else "#1F1E15"
        radius: 16
        padding: 0, 0, 0, "16dp"

        MDListItem:
            theme_bg_color: "Custom"
            md_bg_color: root.md_bg_color
            radius: root.radius
            ripple_effect: False

            MDListItemLeadingAvatar:
                source: root.avatar
                # radius: self.height / 2

            MDListItemHeadlineText:
                text: root.name
                theme_text_color: "Custom"
                text_color: "#8A8D79"

            MDListItemSupportingText:
                text: root.time
                theme_text_color: "Custom"
                text_color: "#8A8D79"

        MDLabel:
            text: root.text
            adaptive_height: True
            theme_text_color: "Custom"
            text_color: "#8A8D79"
            padding_x: "16dp"
            shorten: True
            shorten_from: "right"

        Widget:


    MDFloatLayout:
        md_bg_color: "#151511"

        RecycleView:
            id: card_list
            viewclass: "UserCard"

            SelectableRecycleGridLayout:
                orientation: 'vertical'
                spacing: "16dp"
                padding: "16dp"
                default_size: None, dp(120)
                default_size_hint: 1, None
                size_hint_y: None
                height: self.minimum_height
                multiselect: True
                touch_multiselect: True

        MDBottomAppBar:
            id: bottom_appbar
            scroll_cls: card_list
            allow_hidden: True
            theme_bg_color: "Custom"
            md_bg_color: "#232217"

            MDFabBottomAppBarButton:
                id: fab_button
                icon: "plus"
                theme_bg_color: "Custom"
                md_bg_color: "#373A22"
                theme_icon_color: "Custom"
                icon_color: "#ffffff"
    '''


    class UserCard(RecycleDataViewBehavior, MDBoxLayout):
        name = StringProperty()
        time = StringProperty()
        text = StringProperty()
        avatar = StringProperty()
        callback = ObjectProperty(lambda x: x)

        index = None
        selected = BooleanProperty(False)
        selectable = BooleanProperty(True)

        def refresh_view_attrs(self, rv, index, data):
            self.index = index
            return super().refresh_view_attrs(rv, index, data)

        def on_touch_down(self, touch):
            if super().on_touch_down(touch):
                return True
            if self.collide_point(*touch.pos) and self.selectable:
                Clock.schedule_once(self.callback)
                return self.parent.select_with_touch(self.index, touch)

        def apply_selection(self, rv, index, is_selected):
            self.selected = is_selected
            rv.data[index]["selected"] = is_selected


    class SelectableRecycleGridLayout(
        FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout
    ):
        pass


    class BottomAppBarButton(MDActionBottomAppBarButton):
        theme_icon_color = "Custom"
        icon_color = "#8A8D79"


    class Example(MDApp):
        selected_cards = False

        def build(self):
            return Builder.load_string(KV)

        def on_tap_card(self, *args):
            datas = [data["selected"] for data in self.root.ids.card_list.data]
            if True in datas and not self.selected_cards:
                self.root.ids.bottom_appbar.action_items = [
                    BottomAppBarButton(icon="gmail"),
                    BottomAppBarButton(icon="label-outline"),
                    BottomAppBarButton(icon="bookmark"),
                ]
                self.root.ids.fab_button.icon = "pencil"
                self.selected_cards = True
            else:
                if len(list(set(datas))) == 1 and not list(set(datas))[0]:
                    self.selected_cards = False
                if not self.selected_cards:
                    self.root.ids.bottom_appbar.action_items = [
                        BottomAppBarButton(icon="magnify"),
                        BottomAppBarButton(icon="trash-can-outline"),
                        BottomAppBarButton(icon="download-box-outline"),
                    ]
                    self.root.ids.fab_button.icon = "plus"

        def on_start(self):
            async def generate_card():
                for i in range(10):
                    await asynckivy.sleep(0)
                    self.root.ids.card_list.data.append(
                        {
                            "name": fake.name(),
                            "time": fake.date(),
                            "avatar": fake.image_url(),
                            "text": fake.text(),
                            "selected": False,
                            "callback": self.on_tap_card,
                        }
                    )

            self.on_tap_card()
            fake = Faker()
            Clock.schedule_once(lambda x: asynckivy.start(generate_card()))


    Example().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-app-bar-m3-style-4.gif
    :align: center

API break
=========

1.2.0 version
-------------

.. code-block:: kv

    MDTopAppBar:
        type_height: "large"
        headline_text: "Headline"
        left_action_items: [["arrow-left", lambda x: x]]
        right_action_items:
            [ \
            ["attachment", lambda x: x], \
            ["calendar", lambda x: x], \
            ["dots-vertical", lambda x: x], \
            ]
        anchor_title: "left"

2.0.0 version
-------------

.. code-block:: kv

    MDTopAppBar:
        type: "large"

        MDTopAppBarLeadingButtonContainer:

            MDActionTopAppBarButton:
                icon: "arrow-left"

        MDTopAppBarTitle:
            text: "AppBar small"

        MDTopAppBarTrailingButtonContainer:

            MDActionTopAppBarButton:
                icon: "attachment"

            MDActionTopAppBarButton:
                icon: "calendar"

            MDActionTopAppBarButton:
                icon: "dots-vertical"
"""

from __future__ import annotations

__all__ = (
    "MDTopAppBar",
    "MDTopAppBarTitle",
    "MDBottomAppBar",
    "MDActionTopAppBarButton",
    "MDActionBottomAppBarButton",
    "MDFabBottomAppBarButton",
    "MDTopAppBarLeadingButtonContainer",
    "MDTopAppBarTrailingButtonContainer",
)

import os

from kivy import Logger
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
from kivy.properties import (
    BooleanProperty,
    ListProperty,
    NumericProperty,
    ObjectProperty,
    OptionProperty,
    StringProperty,
    ColorProperty,
)
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.widget import Widget

from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
    CommonElevationBehavior,
    DeclarativeBehavior,
    RotateBehavior,
    ScaleBehavior,
    BackgroundColorBehavior,
)
from kivymd.uix.button import MDFabButton, MDIconButton
from kivymd.uix.controllers import WindowController
from kivymd.uix.label import MDLabel
from kivymd.utils.set_bars_colors import set_bars_colors

import asynckivy

with open(
    os.path.join(uix_path, "appbar", "appbar.kv"), encoding="utf-8"
) as kv_file:
    Builder.load_string(kv_file.read())


class BaseTopAppBarButtonContainer(DeclarativeBehavior, BoxLayout):
    # kivymd.uix.appbar.appbar.MDTopAppBar object.
    _appbar = ObjectProperty()

    def add_widget(self, widget, *args, **kwargs):
        if isinstance(widget, MDActionTopAppBarButton):
            Clock.schedule_once(lambda x: self._check_icon_color(widget))

        return super().add_widget(widget)

    def _check_icon_color(self, widget):
        if widget.theme_icon_color == "Primary" and widget.icon_color == None:
            widget.theme_icon_color = "Custom"
            widget.icon_color = widget.theme_cls.onSurfaceColor


class MDFabBottomAppBarButton(MDFabButton, RotateBehavior, ScaleBehavior):
    """
    Implements a floating action button (FAB) for a bar with type 'bottom'.

    For more information, see in the
    :class:`~kivymd.uix.button.button.MDFabButton` and
    :class:`~kivymd.uix.behaviors.rotate_behavior.RotateBehavior` and
    :class:`~kivymd.uix.behaviors.scale_behavior.ScaleBehavior` and
    classes documentation.
    """


class MDActionTopAppBarButton(MDIconButton):
    """
    Implements action buttons on the bar.

    For more information, see in the
    :class:`~kivymd.uix.button.button.MDIconButton` class documentation.
    """

    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`.
    """


class MDActionBottomAppBarButton(MDActionTopAppBarButton):
    """
    Implements action buttons for a
    :class:'~kivymd.uix.appbar.appbar.MDBottomAppBar' class.

    .. versionadded:: 1.2.0

    For more information, see in the
    :class:`~kivymd.uix.appbar.appbar.MDActionTopAppBarButton`
    class documentation.
    """


class MDTopAppBarTitle(MDLabel):
    """
    Implements the panel title.

    .. versionadded:: 2.0.0

    For more information, see in the
    :class:`~kivymd.uix.label.label.MDLabel` class documentation.
    """

    _appbar = ObjectProperty()
    _title_width = NumericProperty(0)

    # FIXME: When changing the text, the texture_size property returns an
    #  incorrect value, which causes the panel title to shift.
    def on_text(self, instance, value) -> None:
        """Fired when the :attr:`text` value changes."""

        def set_title_width(*args) -> None:
            self._title_width = self.texture_size[0]

        Clock.schedule_once(set_title_width)

    def on_pos_hint(self, instance, value) -> None:
        """Fired when the :attr:`pos_hint` value changes."""

        if self._appbar:
            Clock.schedule_once(
                lambda x: self._appbar._set_padding_title(value)
            )


class MDTopAppBarLeadingButtonContainer(BaseTopAppBarButtonContainer):
    """
    Implements a container for the leading action buttons.

    .. versionadded:: 2.0.0

    For more information, see in the
    :class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
    :class:`~kivy.uix.boxlayout.BoxLayout`
    classes documentation.
    """


class MDTopAppBarTrailingButtonContainer(BaseTopAppBarButtonContainer):
    """
    Implements a container for the trailing action buttons.

    .. versionadded:: 2.0.0

    For more information, see in the
    :class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
    :class:`~kivy.uix.boxlayout.BoxLayout`
    classes documentation.
    """


# FIXME: The on_enter/on_leave event is not triggered for
#  MDActionTopAppBarButton buttons in the MDTopAppBarTrailingButtonContainer
#  container.
#  When the screen size is changed on desktop devices, the position of the
#  trailing container is shifted until the screen size change is completed.
class MDTopAppBar(
    DeclarativeBehavior,
    ThemableBehavior,
    CommonElevationBehavior,
    BackgroundColorBehavior,
    BoxLayout,
    WindowController,
):
    """
    Top app bar class.

    For more information, see in the
    :class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
    :class:`~kivymd.theming.ThemableBehavior` and
    :class:`~kivymd.uix.behaviors.elevation.CommonElevationBehavior` and
    :class:`~kivymd.uix.behaviors.backgroundcolor_behavior.BackgroundColorBehavior` and
    :class:`~kivy.uix.boxlayout.BoxLayout` and
    :class:`~kivymd.uix.controllers.windowcontroller.WindowController`
    classes documentation.

    :Events:
        `on_action_button`
            Method for the button used for the :class:`~MDBottomAppBar` class.
    """

    set_bars_color = BooleanProperty(False)
    """
    If `True` the background color of the bar status will be set automatically
    according to the current color of the bar.

    .. versionadded:: 1.0.0

    See `set_bars_colors <https://kivymd.readthedocs.io/en/latest/api/kivymd/utils/set_bars_colors/>`_
    for more information.

    :attr:`set_bars_color` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    type = OptionProperty("small", options=["medium", "large", "small"])
    """
    Bar height type.

    .. versionadded:: 1.0.0

    Available options are: 'medium', 'large', 'small'.

    :attr:`type_height` is an :class:`~kivy.properties.OptionProperty`
    and defaults to `'small'`.
    """

    _trailing_button_container = ObjectProperty()
    _leading_button_container = ObjectProperty()
    _appbar_title = ObjectProperty()
    _right_padding = NumericProperty(0)
    _left_padding = NumericProperty(0)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        Clock.schedule_once(lambda x: self.on_size(self, (0, 0)))

    def on_type(self, instance, value) -> None:
        def on_type(*args):
            if value in ("medium", "large"):
                self.ids.root_box.add_widget(Widget(), index=1)

        Clock.schedule_once(on_type, 0.5)

    def on_size(self, instance, size) -> None:
        """Fired when the application screen size changes."""

        if self._appbar_title:
            if not self._appbar_title._title_width:
                self._appbar_title._title_width = (
                    self._appbar_title.texture_size[0]
                )
            self._right_padding = 0
            self._left_padding = 0
            self._appbar_title.on_pos_hint(
                self._appbar_title, self._appbar_title.pos_hint
            )

    def add_widget(self, widget, *args, **kwargs):
        if isinstance(widget, MDTopAppBarTitle):
            widget._appbar = self
            self._appbar_title = widget
            widget.bind(text=lambda *x: self.on_size(*x))
            Clock.schedule_once(lambda x: self._add_title(widget))
        elif isinstance(widget, MDTopAppBarTrailingButtonContainer):
            self._trailing_button_container = widget
            widget._appbar = self
            Clock.schedule_once(lambda x: self.ids.root_box.add_widget(widget))
        elif isinstance(widget, MDTopAppBarLeadingButtonContainer):
            widget._appbar = self
            self._leading_button_container = widget
            Clock.schedule_once(lambda x: self.ids.root_box.add_widget(widget))
        else:
            return super().add_widget(widget)

    def _add_title(self, widget):
        if self.type == "small":
            self.ids.root_box.add_widget(widget)
        else:
            self.ids.title_box.add_widget(widget)

    def _set_padding_title(self, value):
        if value.get("center_x", 0) == 0.5 and self.type == "small":
            if (
                not self._trailing_button_container
                and self._leading_button_container
            ):
                self._left_padding = (
                    (self.width // 2)
                    - (
                        self._leading_button_container.width
                        + (self._appbar_title._title_width // 2)
                    )
                ) - self._left_padding
            elif (
                self._trailing_button_container
                and not self._leading_button_container
            ):
                self._left_padding = (
                    (self.width // 2) - (self._appbar_title._title_width // 2)
                ) - self._left_padding
                self._right_padding = (
                    (self.width // 2)
                    - (
                        self._trailing_button_container.width
                        + (self._appbar_title._title_width // 2)
                    )
                ) - self._right_padding
            elif (
                not self._trailing_button_container
                and not self._leading_button_container
            ):
                self._left_padding = (
                    (self.width // 2) - (self._appbar_title._title_width // 2)
                ) - self._left_padding
                self._right_padding = (
                    (self.width // 2) - (self._appbar_title._title_width // 2)
                ) - self._right_padding
            elif (
                self._trailing_button_container
                and self._leading_button_container
            ):
                self._left_padding = (
                    (self.width // 2)
                    - (
                        self._leading_button_container.width
                        + (self._appbar_title._title_width // 2)
                    )
                ) - self._left_padding
                self._right_padding = (
                    (self.width // 2)
                    - (
                        self._trailing_button_container.width
                        + (self._appbar_title._title_width // 2)
                    )
                ) - self._right_padding
        elif (
            not value
            and self._trailing_button_container
            and self._leading_button_container
        ):
            if self.type == "small":

                self._right_padding = (
                    self.width
                    - (
                        self._trailing_button_container.width
                        + self._leading_button_container.width
                        + self._appbar_title._title_width
                    )
                    - self._right_padding
                )
        elif (
            not value
            and self._trailing_button_container
            and not self._leading_button_container
        ):
            if self.type == "small":
                self._right_padding = (
                    self.width
                    - (
                        self._trailing_button_container.width
                        + self._appbar_title._title_width
                    )
                    - self._right_padding
                )
                self._left_padding = dp(16)
        elif (
            not value
            and not self._trailing_button_container
            and not self._leading_button_container
        ):
            self._left_padding = dp(16)


class MDBottomAppBar(
    DeclarativeBehavior,
    ThemableBehavior,
    BackgroundColorBehavior,
    CommonElevationBehavior,
    FloatLayout,
):
    """
    Bottom app bar class.

    For more information, see in the
    :class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
    :class:`~kivymd.theming.ThemableBehavior` and
    :class:`~kivymd.uix.behaviors.backgroundcolor_behavior.BackgroundColorBehavior` and
    :class:`~kivymd.uix.behaviors.elevation.CommonElevationBehavior` and
    :class:`~kivy.uix.floatlayout.FloatLayout`
    classes documentation.

    :Events:
        `on_show_bar`
            The method is fired when the :class:`~MDBottomAppBar` panel
            is shown.
        `on_hide_bar`
            The method is fired when the :class:`~MDBottomAppBar` panel
            is hidden.
    """

    action_items = ListProperty()
    """
    The icons on the left bar.

    .. versionadded:: 1.2.0

    :attr:`action_items` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[]`.
    """

    animation = BooleanProperty(True)
    """
    # TODO: add description.
    # FIXME: changing the value does not affect anything.

    .. versionadded:: 1.2.0

    :attr:`animation` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    show_transition = StringProperty("linear")
    """
    Type of button display transition.

    .. versionadded:: 1.2.0

    :attr:`show_transition` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'linear'`.
    """

    hide_transition = StringProperty("in_back")
    """
    Type of button hidden transition.

    .. versionadded:: 1.2.0

    :attr:`hide_transition` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'in_back'`.
    """

    hide_duration = NumericProperty(0.4)
    """
    Duration of button hidden transition.

    .. versionadded:: 1.2.0

    :attr:`hide_duration` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0.2`.
    """

    show_duration = NumericProperty(0.2)
    """
    Duration of button display transition.

    .. versionadded:: 1.2.0

    :attr:`show_duration` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0.2`.
    """

    scroll_cls = ObjectProperty()
    """
    Widget inherited from the :class:`~kivy.uix.scrollview.ScrollView` class.
    The value must be set if the :attr:`allow_hidden` parameter is `True`.

    .. versionadded:: 1.2.0

    :attr:`scroll_cls` is a :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    allow_hidden = BooleanProperty(False)
    """
    Allows or disables hiding the panel when scrolling content.
    If the value is `True`, the :attr:`scroll_cls` parameter must be specified.

    .. versionadded:: 1.2.0

    :attr:`allow_hidden` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    bar_is_hidden = BooleanProperty(False)
    """
    Is the panel currently hidden.

    .. versionadded:: 1.2.0

    :attr:`bar_is_hidden` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    _padding = dp(16)
    _x = -dp(48)
    _scroll_cls_y = 0
    _cache = []
    _current_data = []
    _wait_removed = False
    _animated_hidden = True
    _animated_show = True
    _fab_bottom_app_bar_button = None
    _action_overflow_button = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.register_event_type("on_show_bar")
        self.register_event_type("on_hide_bar")

    def button_centering_animation(
        self,
        button: MDActionBottomAppBarButton | MDFabBottomAppBarButton,
    ) -> None:
        """
        Animation of centering buttons for
        :class:`~MDActionOverFlowButton`,
        :class:`~MDActionBottomAppBarButton` and
        :class:`~MDFabBottomAppBarButton` classes.
        """

        if self.animation:
            Animation(
                y=self.height / 2 - dp(48) / 2,
                opacity=1,
                d=self.show_duration,
                t=self.show_transition,
            ).start(button)

    def check_scroll_direction(self, scroll_cls, y: float) -> None:
        """
        Checks the scrolling direction.
        Depending on the scrolling direction, hides or shows the
        :class:`~MDBottomAppBar` panel.
        """

        if round(y, 1) < self._scroll_cls_y and not self.bar_is_hidden:
            self.hide_bar()
        if round(y, 1) > self._scroll_cls_y and self.bar_is_hidden:
            self.show_bar()

        self._scroll_cls_y = round(y, 1)

    def show_bar(self) -> None:
        """Show :class:`~MDBottomAppBar` panel."""

        def on_complete(*args):
            self.dispatch("on_show_bar")

        def on_progress(animation, instance, progress):
            if progress > 0.5 and self._animated_show:
                self._animated_show = False
                for i, widget in enumerate(self.children):
                    if isinstance(widget, MDActionBottomAppBarButton):
                        anim_icon = Animation(
                            y=self.height / 2 - dp(48) / 2,
                            d=self.show_duration,
                            t=self.show_transition,
                        )
                        Clock.schedule_once(
                            lambda x, y=widget: anim_icon.start(y),
                            i / 10,
                        )
                if self._fab_bottom_app_bar_button:
                    Animation(
                        y=self._fab_bottom_app_bar_button.y + dp(4),
                        d=self.show_duration,
                        t=self.show_transition,
                    ).start(self._fab_bottom_app_bar_button)

        self.bar_is_hidden = False
        self._animated_show = True
        anim = Animation(
            y=0,
            d=self.show_duration,
            t=self.show_transition,
        )
        anim.bind(on_progress=on_progress, on_complete=on_complete)
        anim.start(self)

    def hide_bar(self) -> None:
        """Hide :class:`~MDBottomAppBar` panel."""

        def on_complete(*args):
            self.dispatch("on_hide_bar")

        def on_progress(animation, instance, progress):
            if (
                progress > 0.5
                and self._animated_hidden
                and widget_icon == instance.icon
            ):
                self._animated_hidden = False
                anim_bar = Animation(
                    y=-self.height,
                    d=self.hide_duration,
                    # t=self.hide_transition,
                )
                anim_bar.bind(on_complete=on_complete)
                anim_bar.start(self)

                if self._fab_bottom_app_bar_button:
                    Animation(
                        y=self._fab_bottom_app_bar_button.y - dp(4),
                        d=self.hide_duration,
                        t=self.hide_transition,
                    ).start(self._fab_bottom_app_bar_button)

        self.bar_is_hidden = True
        self._animated_hidden = True
        len_children = len(self.children)
        widget_icon = ""

        for i, widget in enumerate(self.children):
            if isinstance(widget, MDActionBottomAppBarButton):
                anim = Animation(
                    y=-widget.height,
                    d=self.hide_duration,
                    t=self.hide_transition,
                )
                if i + 2 == len_children:
                    widget_icon = widget.icon
                    anim.bind(on_progress=on_progress)
                Clock.schedule_once(
                    lambda x, y=widget: anim.start(y),
                    i / 10,
                )

    def on_show_bar(self, *args) -> None:
        """
        The method is fired when the :class:`~MDBottomAppBar` panel
        is shown.
        """

    def on_hide_bar(self, *args) -> None:
        """
        The method is fired when the :class:`~MDBottomAppBar` panel
        is hidden.
        """

    def on_scroll_cls(self, instance, scroll_cls) -> None:
        """
        Fired when the value of the :attr:`scroll_cls` attribute changes.
        """

        def on_scroll_cls(*args):
            if not self.allow_hidden:
                Logger.warning(
                    "KivyMD: "
                    "In order for the bottom bar to be automatically hidden "
                    "in addition to the `scroll_cls` parameter, set the value "
                    "of the `allow_hidden` parameter to `True`"
                )

            if issubclass(scroll_cls.__class__, ScrollView):
                if self.allow_hidden:
                    scroll_cls.bind(scroll_y=self.check_scroll_direction)
            else:
                raise TypeError(
                    f"The `scroll_cls` parameter must be an object inherited "
                    f"from the {ScrollView} class"
                )

        Clock.schedule_once(on_scroll_cls)

    def on_size(self, *args) -> None:
        """Fired when the root screen is resized."""

        if self._fab_bottom_app_bar_button:
            self._fab_bottom_app_bar_button.x = Window.width - (dp(56) + dp(16))

    def on_action_items(self, instance, value: list) -> None:
        """
        Fired when the value of the :attr:`action_items` attribute changes.
        """

        def wait_removed(*args):
            if len(self.children) == 1 or not self.children:
                Clock.unschedule(wait_removed)
                self._wait_removed = False
                self._x = -dp(48)
                asynckivy.start(add_widget())

        async def add_widget():
            for button in value:
                await asynckivy.sleep(0)
                self.add_widget(button)

        if self._cache:
            self._cache.append(value)

            for data in self._cache:
                if value[0] in data:
                    for i, widget in enumerate(self.children):
                        if not self._wait_removed:
                            Clock.schedule_interval(wait_removed, 0)
                            self._wait_removed = True
                        if isinstance(widget, MDActionBottomAppBarButton):
                            anim = Animation(
                                y=-widget.height,
                                d=self.hide_duration,
                                t=self.hide_transition,
                            )
                            anim.bind(
                                on_complete=lambda x, y=widget: self.remove_widget(
                                    y
                                )
                            )
                            Clock.schedule_once(
                                lambda x, y=widget: anim.start(y),
                                i / 10,
                            )
        else:
            self._cache.append(value)
            self._current_data = value
            asynckivy.start(add_widget())

    def set_fab_opacity(self, *ars) -> None:
        """
        Sets the transparency value of the:class:`~MDFabBottomAppBarButton`
        button.
        """

        # self._fab_bottom_app_bar_button.opacity = 1

    def set_fab_icon(self, instance, value) -> None:
        """
        Animates the size of the :class:`~MDFabBottomAppBarButton` button.
        """

        # self._fab_bottom_app_bar_button.opacity = 0
        anim = Animation(
            scale_value_x=0,
            scale_value_y=0,
            opacity=0,
            d=self.hide_duration,
            t=self.hide_transition,
        ) + Animation(
            scale_value_x=1,
            scale_value_y=1,
            opacity=1,
            d=self.show_duration,
            t=self.show_transition,
        )
        anim.bind(on_complete=self.set_fab_opacity)
        anim.start(instance)

    def add_widget(self, widget, index=0, canvas=None):
        if isinstance(widget, MDActionBottomAppBarButton):
            self._x += widget.width
            widget.pos = (
                self._x + self._padding,
                -dp(48) if self.animation else self.height / 2 - dp(48) / 2,
            )
            widget.opacity = int(not self.animation)
            super().add_widget(widget)
            self.button_centering_animation(widget)
        elif isinstance(widget, MDFabBottomAppBarButton):
            widget.bind(icon=self.set_fab_icon)
            self._fab_bottom_app_bar_button = widget
            Clock.schedule_once(self.set_fab_opacity)
            widget.scale_value_x = int(not self.animation)
            widget.scale_value_y = int(not self.animation)
            widget.pos = (
                Window.width - (dp(56) + self._padding),
                self.height / 2 - dp(56) / 2,
            )
            super().add_widget(widget)
