"""
Components/NavigationDrawer
===========================

.. seealso::

    `Material Design, Navigation drawer <https://m3.material.io/components/navigation-drawer/overview>`_

.. rubric:: Navigation drawers provide access to destinations in your app.

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer.png
    :align: center

- Use navigation drawers in expanded layouts and modal navigation drawers in compact and medium layouts
- Can be open or closed by default
- Two types: standard and modal

When using the class :class:`~MDNavigationDrawer` skeleton of your `KV` markup
should look like this:

Usage
-----

.. code-block:: kv

    Root:

        MDNavigationLayout:

            MDScreenManager:

                Screen_1:

                Screen_2:

            MDNavigationDrawer:

                # This custom rule should implement what will be displayed in
                # your MDNavigationDrawer.
                ContentNavigationDrawer:

A simple example
----------------

.. tabs::

    .. tab:: Declarative KV styles

        .. code-block:: python

            from kivy.lang import Builder

            from kivymd.app import MDApp

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

                MDNavigationLayout:

                    MDScreenManager:

                        MDScreen:

                            MDButton:
                                pos_hint: {"center_x": .5, "center_y": .5}
                                on_release: nav_drawer.set_state("toggle")

                                MDButtonText:
                                    text: "Open Drawer"

                    MDNavigationDrawer:
                        id: nav_drawer
                        radius: 0, dp(16), dp(16), 0

                        MDNavigationDrawerMenu:

                            MDNavigationDrawerLabel:
                                text: "Mail"

                            MDNavigationDrawerItem:

                                MDNavigationDrawerItemLeadingIcon:
                                    icon: "account"

                                MDNavigationDrawerItemText:
                                    text: "Inbox"

                                MDNavigationDrawerItemTrailingText:
                                    text: "24"

                            MDNavigationDrawerDivider:
            '''


            class Example(MDApp):
                def build(self):
                    return Builder.load_string(KV)


            Example().run()

    .. tab:: Declarative python styles

        .. code-block:: python

            from kivy.metrics import dp

            from kivymd.uix.button import MDButton, MDButtonText
            from kivymd.uix.screenmanager import MDScreenManager
            from kivymd.uix.navigationdrawer import (
                MDNavigationLayout,
                MDNavigationDrawer,
                MDNavigationDrawerMenu,
                MDNavigationDrawerLabel,
                MDNavigationDrawerItem,
                MDNavigationDrawerItemLeadingIcon,
                MDNavigationDrawerItemText,
                MDNavigationDrawerItemTrailingText,
                MDNavigationDrawerDivider,
            )
            from kivymd.uix.screen import MDScreen
            from kivymd.app import MDApp


            class Example(MDApp):
                def build(self):
                    return MDScreen(
                        MDNavigationLayout(
                            MDScreenManager(
                                MDScreen(
                                    MDButton(
                                        MDButtonText(
                                            text="Open Drawer",
                                        ),
                                        on_release=lambda x: self.root.get_ids().nav_drawer.set_state(
                                            "toggle"
                                        ),
                                        pos_hint={"center_x": 0.5, "center_y": 0.5},
                                    ),
                                ),
                            ),
                            MDNavigationDrawer(
                                MDNavigationDrawerMenu(
                                    MDNavigationDrawerLabel(
                                        text="Mail",
                                    ),
                                    MDNavigationDrawerItem(
                                        MDNavigationDrawerItemLeadingIcon(
                                            icon="account",
                                        ),
                                        MDNavigationDrawerItemText(
                                            text="Inbox",
                                        ),
                                        MDNavigationDrawerItemTrailingText(
                                            text="24",
                                        ),
                                    ),
                                    MDNavigationDrawerDivider(
                                    ),
                                ),
                                id="nav_drawer",
                                radius=(0, dp(16), dp(16), 0),
                            ),
                        ),
                        md_bg_color=self.theme_cls.backgroundColor,
                    )


            Example().run()

Anatomy
-------

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

.. Note:: :class:`~MDNavigationDrawer` is an empty
    :class:`~kivymd.uix.card.MDCard` panel.

Item anatomy
------------

.. code-block:: kv

    MDNavigationDrawerItem:

        MDNavigationDrawerItemLeadingIcon:
            icon: "account"

        MDNavigationDrawerItemText:
            text: "Inbox"

        MDNavigationDrawerItemTrailingText:
            text: "24"

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-item-anatomy.png
    :align: center

Type drawer
===========

Standard
--------

.. code-block:: kv

    MDNavigationDrawer:
        drawer_type: "standard"

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-type-standard.gif
    :align: center

Modal
-----

.. code-block:: kv

    MDNavigationDrawer:
        drawer_type: "modal"

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-type-modal.gif
    :align: center

Anchoring screen edge for drawer
================================

    Left
    ----

    .. code-block:: kv

        MDNavigationDrawer:
            anchor: "left"

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-ancjor-left.png
        :align: center

    Right
    -----

    .. code-block:: kv

        MDNavigationDrawer:
            anchor: "right"

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-ancjor-right.png
        :align: center

API break
=========

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

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp

    KV = '''
    <DrawerClickableItem@MDNavigationDrawerItem>
        focus_color: "#e7e4c0"
        text_color: "#4a4939"
        icon_color: "#4a4939"
        ripple_color: "#c5bdd2"
        selected_color: "#0c6c4d"


    <DrawerLabelItem@MDNavigationDrawerItem>
        text_color: "#4a4939"
        icon_color: "#4a4939"
        focus_behavior: False
        selected_color: "#4a4939"
        _no_ripple_effect: True


    MDScreen:

        MDNavigationLayout:

            MDScreenManager:

                MDScreen:

                    MDRaisedButton:
                        text: "Open Drawer"
                        pos_hint: {"center_x": .5, "center_y": .5}
                        on_release: nav_drawer.set_state("toggle")

            MDNavigationDrawer:
                id: nav_drawer
                radius: (0, dp(16), dp(16), 0)

                MDNavigationDrawerMenu:

                    MDNavigationDrawerHeader:
                        title: "Header title"
                        title_color: "#4a4939"
                        text: "Header text"
                        spacing: "4dp"
                        padding: "12dp", 0, 0, "56dp"

                    MDNavigationDrawerLabel:
                        text: "Mail"

                    DrawerClickableItem:
                        icon: "gmail"
                        right_text: "+99"
                        text_right_color: "#4a4939"
                        text: "Inbox"

                    DrawerClickableItem:
                        icon: "send"
                        text: "Outbox"

                    MDNavigationDrawerDivider:

                    MDNavigationDrawerLabel:
                        text: "Labels"

                    DrawerLabelItem:
                        icon: "information-outline"
                        text: "Label"

                    DrawerLabelItem:
                        icon: "information-outline"
                        text: "Label"
    '''


    class Example(MDApp):
        def build(self):
            return Builder.load_string(KV)


    Example().run()

2.2.0 version
-------------

.. code-block:: python

    from kivy.lang import Builder
    from kivy.properties import StringProperty, ColorProperty

    from kivymd.app import MDApp
    from kivymd.uix.boxlayout import MDBoxLayout
    from kivymd.uix.navigationdrawer import (
        MDNavigationDrawerItem, MDNavigationDrawerItemTrailingText
    )

    KV = '''
    <DrawerItem>
        active_indicator_color: "#e7e4c0"

        MDNavigationDrawerItemLeadingIcon:
            icon: root.icon
            theme_icon_color: "Custom"
            icon_color: "#4a4939"

        MDNavigationDrawerItemText:
            text: root.text
            theme_text_color: "Custom"
            text_color: "#4a4939"


    <DrawerLabel>
        adaptive_height: True
        padding: "18dp", 0, 0, "12dp"

        MDNavigationDrawerItemLeadingIcon:
            icon: root.icon
            theme_icon_color: "Custom"
            icon_color: "#4a4939"
            pos_hint: {"center_y": .5}

        MDNavigationDrawerLabel:
            text: root.text
            theme_text_color: "Custom"
            text_color: "#4a4939"
            pos_hint: {"center_y": .5}
            padding: "6dp", 0, "16dp", 0
            theme_line_height: "Custom"
            line_height: 0


    MDScreen:
        md_bg_color: self.theme_cls.backgroundColor

        MDNavigationLayout:

            MDScreenManager:

                MDScreen:

                    MDButton:
                        pos_hint: {"center_x": .5, "center_y": .5}
                        on_release: nav_drawer.set_state("toggle")

                        MDButtonText:
                            text: "Open Drawer"

            MDNavigationDrawer:
                id: nav_drawer
                radius: 0, dp(16), dp(16), 0

                MDNavigationDrawerMenu:

                    MDNavigationDrawerHeader:
                        orientation: "vertical"
                        padding: 0, 0, 0, "12dp"
                        adaptive_height: True

                        MDLabel:
                            text: "Header title"
                            theme_text_color: "Custom"
                            theme_line_height: "Custom"
                            line_height: 0
                            text_color: "#4a4939"
                            adaptive_height: True
                            padding_x: "16dp"
                            font_style: "Display"
                            role: "small"

                        MDLabel:
                            text: "Header text"
                            padding_x: "18dp"
                            adaptive_height: True
                            font_style: "Title"
                            role: "large"

                    MDNavigationDrawerDivider:

                    DrawerItem:
                        icon: "gmail"
                        text: "Inbox"
                        trailing_text: "+99"
                        trailing_text_color: "#4a4939"

                    DrawerItem:
                        icon: "send"
                        text: "Outbox"

                    MDNavigationDrawerDivider:

                    MDNavigationDrawerLabel:
                        text: "Labels"
                        padding_y: "12dp"

                    DrawerLabel:
                        icon: "information-outline"
                        text: "Label"

                    DrawerLabel:
                        icon: "information-outline"
                        text: "Label"
    '''


    class DrawerLabel(MDBoxLayout):
        icon = StringProperty()
        text = StringProperty()


    class DrawerItem(MDNavigationDrawerItem):
        icon = StringProperty()
        text = StringProperty()
        trailing_text = StringProperty()
        trailing_text_color = ColorProperty()

        _trailing_text_obj = None

        def on_trailing_text(self, instance, value):
            self._trailing_text_obj = MDNavigationDrawerItemTrailingText(
                text=value,
                theme_text_color="Custom",
                text_color=self.trailing_text_color,
            )
            self.add_widget(self._trailing_text_obj)

        def on_trailing_text_color(self, instance, value):
            self._trailing_text_obj.text_color = value


    class Example(MDApp):
        def build(self):
            return Builder.load_string(KV)


    Example().run()
"""

__all__ = (
    "MDNavigationLayout",
    "MDNavigationDrawer",
    "MDNavigationDrawerItem",
    "MDNavigationDrawerItemLeadingIcon",
    "MDNavigationDrawerItemTrailingText",
    "MDNavigationDrawerItemText",
    "MDNavigationDrawerMenu",
    "MDNavigationDrawerHeader",
    "MDNavigationDrawerLabel",
    "MDNavigationDrawerDivider",
    "BaseNavigationDrawerItem",
)

import os

from kivy.animation import Animation, AnimationTransition
from kivy.core.window import Window
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import (
    AliasProperty,
    BooleanProperty,
    ColorProperty,
    NumericProperty,
    ObjectProperty,
    OptionProperty,
    StringProperty,
    VariableListProperty,
)
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager

from kivymd.uix.appbar import MDTopAppBar
from kivymd.uix.behaviors import DeclarativeBehavior
from kivymd.uix.label import MDLabel
from kivymd import uix_path
from kivymd.uix.behaviors.focus_behavior import FocusBehavior
from kivymd.uix.card import MDCard
from kivymd.uix.list import (
    MDListItem,
    MDListItemLeadingIcon,
    MDListItemSupportingText,
    MDListItemTrailingSupportingText,
)
from kivymd.uix.scrollview import MDScrollView

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


class NavigationDrawerContentError(Exception):
    pass


class BaseNavigationDrawerItem:
    """
    Implement the base class for the menu list item.

    .. versionadded:: 2.0.0
    """

    selected = BooleanProperty(False)
    """
    Is the item selected.

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

    # kivymd.uix.navigationdrawer.MDNavigationDrawerMenu object.
    _drawer_menu = ObjectProperty()
    # kivymd.uix.navigationdrawer.MDNavigationDrawerItem object.
    _drawer_item = ObjectProperty()


class MDNavigationLayout(DeclarativeBehavior, FloatLayout):
    """
    For more information, see in the
    :class:`~kivymd.uix.behaviors.declarative_behavior.DeclarativeBehavior` and
    :class:`~kivy.uix.floatlayout.FloatLayout`
    classes documentation.
    """

    _scrim_color = ObjectProperty(None)
    _scrim_rectangle = ObjectProperty(None)
    _screen_manager = ObjectProperty(None)
    _navigation_drawer = ObjectProperty(None)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.bind(width=self.update_pos)

    def update_pos(self, instance_navigation_drawer, pos_x: float) -> None:
        drawer = self._navigation_drawer
        manager = self._screen_manager
        if not drawer or not manager:
            return
        if drawer.drawer_type == "standard":
            manager.size_hint_x = None
            if drawer.anchor == "left":
                if (
                    self._navigation_drawer.__class__.__name__
                    != "MDBottomSheet"
                ):
                    manager.x = drawer.width + drawer.x
                    manager.width = self.width - manager.x
                else:
                    manager.width = self.width - manager.x
            else:
                manager.x = 0
                manager.width = drawer.x
        elif drawer.drawer_type == "modal":
            manager.size_hint_x = None
            manager.x = 0
            if drawer.anchor == "left":
                manager.width = self.width - manager.x
            else:
                manager.width = self.width

    def add_scrim(self, instance_manager: ScreenManager) -> None:
        with instance_manager.canvas.after:
            self._scrim_color = Color(rgba=[0, 0, 0, 0])
            self._scrim_rectangle = Rectangle(
                pos=instance_manager.pos, size=instance_manager.size
            )
            instance_manager.bind(
                pos=self.update_scrim_rectangle,
                size=self.update_scrim_rectangle,
            )

    def update_scrim_rectangle(
        self, instance_manager: ScreenManager, size: list
    ) -> None:
        self._scrim_rectangle.pos = self.pos
        self._scrim_rectangle.size = self.size

    def add_widget(self, widget, index=0, canvas=None):
        """
        Only two layouts are allowed:
        :class:`~kivy.uix.screenmanager.ScreenManager` and
        :class:`~MDNavigationDrawer`.
        """

        if not isinstance(
            widget,
            (MDNavigationDrawer, ScreenManager, MDTopAppBar),
        ):
            raise NavigationDrawerContentError(
                "The MDNavigationLayout must contain "
                "only `MDNavigationDrawer` and `ScreenManager`"
            )
        if isinstance(widget, ScreenManager):
            self._screen_manager = widget
            self.add_scrim(widget)
        if isinstance(widget, MDNavigationDrawer):
            self._navigation_drawer = widget
            widget.bind(
                x=self.update_pos, width=self.update_pos, anchor=self.update_pos
            )
        if len(self.children) > 3:
            raise NavigationDrawerContentError(
                "The MDNavigationLayout must contain "
                "only `MDNavigationDrawer` and `ScreenManager`"
            )
        return super().add_widget(widget)


class MDNavigationDrawerLabel(MDLabel):
    """
    Implements a label class.

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

    .. versionadded:: 1.0.0
    """


class MDNavigationDrawerDivider(BoxLayout):
    """
    Implements a divider class.

    For more information, see in the
    :class:`~kivy.uix.boxlayout.BoxLayout` class documentation.

    .. versionadded:: 1.0.0
    """


class MDNavigationDrawerHeader(DeclarativeBehavior, BoxLayout):
    """
    Implements a header class.

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

    .. versionadded:: 1.0.0
    """


class MDNavigationDrawerItem(
    MDListItem, FocusBehavior, BaseNavigationDrawerItem
):
    """
    Implements an item for the :class:`~MDNavigationDrawer` menu list.

    For more information, see in the
    :class:`~kivymd.uix.list.list.MDListItem` and
    :class:`~kivymd.uix.behaviors.focus_behavior.FocusBehavior` and
    :class:`~BaseNavigationDrawerItem`
    classes documentation.

    .. versionadded:: 1.0.0
    """

    active_indicator_color = ColorProperty(None)
    """
    The active indicator color in (r, g, b, a) or string format.

    .. versionadded:: 2.0.0

    :attr:`active_indicator_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    inactive_indicator_color = ColorProperty(None)
    """
    The inactive indicator color in (r, g, b, a) or string format.

    .. versionadded:: 2.0.0

    :attr:`inactive_indicator_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    def add_widget(self, widget, *args, **kwargs):
        if isinstance(
            widget,
            (
                MDNavigationDrawerItemLeadingIcon,
                MDNavigationDrawerItemText,
                MDNavigationDrawerItemTrailingText,
            ),
        ):
            widget._drawer_item = self
        return super().add_widget(widget)

    def on_release(self, *args) -> None:
        """
        Fired when the item is released
        (i.e. the touch/click that pressed the item goes away).
        """

        self.selected = not self.selected
        self._drawer_menu.update_items_color(self)


class MDNavigationDrawerItemLeadingIcon(
    MDListItemLeadingIcon, BaseNavigationDrawerItem
):
    """
    Implements the leading icon for the menu list item.

    For more information, see in the
    :class:`~kivymd.uix.list.list.MDListItemLeadingIcon` and
    :class:`~BaseNavigationDrawerItem`
    classes documentation.

    .. versionadded:: 2.0.0
    """


class MDNavigationDrawerItemText(
    MDListItemSupportingText, BaseNavigationDrawerItem
):
    """
    Implements the text for the menu list item.

    For more information, see in the
    :class:`~kivymd.uix.list.list.MDListItemSupportingText` and
    :class:`~BaseNavigationDrawerItem`
    classes documentation.

    .. versionadded:: 2.0.0
    """


class MDNavigationDrawerItemTrailingText(
    MDListItemTrailingSupportingText, BaseNavigationDrawerItem
):
    """
    Implements the supporting text for the menu list item.

    For more information, see in the
    :class:`~kivymd.uix.list.list.MDListItemTrailingSupportingText` and
    :class:`~BaseNavigationDrawerItem`
    classes documentation.

    .. versionadded:: 2.0.0
    """


class MDNavigationDrawerMenu(MDScrollView):
    """
    Implements a scrollable list for menu items of the
    :class:`~MDNavigationDrawer` class.

    For more information, see in the
    :class:`~kivymd.uix.scrollview.MDScrollView` class documentation.

    .. versionadded:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

                # Your menu items.
                ...
    """

    spacing = NumericProperty(0)
    """
    Spacing between children, in pixels.

    :attr:`spacing` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0`.
    """

    def add_widget(self, widget, *args, **kwargs):
        if isinstance(widget, GridLayout):
            return super().add_widget(widget, *args, **kwargs)
        else:
            if isinstance(widget, MDNavigationDrawerItem):
                widget._drawer_menu = self
            self.ids.menu.add_widget(widget)

    def update_items_color(self, item: MDNavigationDrawerItem) -> None:
        for widget in self.ids.menu.children:
            if issubclass(widget.__class__, MDNavigationDrawerItem):
                if widget is not item:
                    widget.md_bg_color = (
                        widget.theme_cls.surfaceContainerLowColor
                        if not widget.inactive_indicator_color
                        else widget.inactive_indicator_color
                    )
                else:
                    widget.md_bg_color = (
                        widget.theme_cls.secondaryContainerColor
                        if not widget.active_indicator_color
                        else widget.active_indicator_color
                    )


class MDNavigationDrawer(MDCard):
    """
    Navigation drawer class.

    For more information, see in the :class:`~kivymd.uix.card.card.MDCard`
    class documentation.

    :Events:

        .. versionadded:: 2.0.0

        `on_open`:
            Fired when the navigation drawer is opened.
        `on_close`:
            Fired when the navigation drawer is closed.
    """

    drawer_type = OptionProperty("modal", options=("standard", "modal"))
    """
    Type of drawer. Modal type will be on top of screen. Standard type will be
    at left or right of screen. Also it automatically disables
    :attr:`close_on_click` and :attr:`enable_swiping` to prevent closing
    drawer for standard type.

    .. versionchanged:: 2.0.0

        Rename from `type` to `drawer_type`.

    :attr:`drawer_type` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `'modal'`.
    """

    anchor = OptionProperty("left", options=("left", "right"))
    """
    Anchoring screen edge for drawer. Set it to `'right'` for right-to-left
    languages. Available options are: `'left'`, `'right'`.

    :attr:`anchor` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `'left'`.
    """

    # FIXME: Doesn't work in Kivy v2.1.0.
    scrim_color = ColorProperty([0, 0, 0, 0.5])
    """
    Color for scrim in (r, g, b, a) or string format. Alpha channel will be
    multiplied with :attr:`_scrim_alpha`. Set fourth channel to 0 if you want
    to disable scrim.

    :attr:`scrim_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `[0, 0, 0, 0.5]`.
    """

    padding = VariableListProperty([dp(16), dp(16), dp(12), dp(16)])
    """
    Padding between layout box and children: [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].

    .. versionchanged:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:
            padding: "56dp"

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-padding.png
        :align: center

    :attr:`padding` is a :class:`~kivy.properties.VariableListProperty` and
    defaults to '[dp(16), dp(16), dp(12), dp(16)]'.
    """

    close_on_click = BooleanProperty(True)
    """
    Close when click on scrim or keyboard escape. It automatically sets to
    False for "standard" type.

    :attr:`close_on_click` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    state = OptionProperty("close", options=("close", "open"))
    """
    Indicates if panel closed or opened. Sets after :attr:`status` change.
    Available options are: `'close'`, `'open'`.

    :attr:`state` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `'close'`.
    """

    status = OptionProperty(
        "closed",
        options=(
            "closed",
            "opening_with_swipe",
            "opening_with_animation",
            "opened",
            "closing_with_swipe",
            "closing_with_animation",
        ),
    )
    """
    Detailed state. Sets before :attr:`state`. Bind to :attr:`state` instead
    of :attr:`status`. Available options are: `'closed'`,
    `'opening_with_swipe'`, `'opening_with_animation'`, `'opened'`,
    `'closing_with_swipe'`, `'closing_with_animation'`.

    :attr:`status` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `'closed'`.
    """

    open_progress = NumericProperty(0.0)
    """
    Percent of visible part of side panel. The percent is specified as a
    floating point number in the range 0-1. 0.0 if panel is closed and 1.0 if
    panel is opened.

    :attr:`open_progress` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0.0`.
    """

    enable_swiping = BooleanProperty(True)
    """
    Allow to open or close navigation drawer with swipe. It automatically
    sets to False for "standard" type.

    :attr:`enable_swiping` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    swipe_distance = NumericProperty(10)
    """
    The distance of the swipe with which the movement of navigation drawer
    begins.

    :attr:`swipe_distance` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `10`.
    """

    swipe_edge_width = NumericProperty(20)
    """
    The size of the area in px inside which should start swipe to drag
    navigation drawer.

    :attr:`swipe_edge_width` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `20`.
    """

    def _get_scrim_alpha(self):
        _scrim_alpha = 0
        if self.drawer_type == "modal":
            _scrim_alpha = self._scrim_alpha_transition(self.open_progress)
        if (
            isinstance(self.parent, MDNavigationLayout)
            and self.parent._scrim_color
        ):
            self.parent._scrim_color.rgba = self.scrim_color[:3] + [
                self.scrim_color[3] * _scrim_alpha
            ]
        return _scrim_alpha

    _scrim_alpha = AliasProperty(
        _get_scrim_alpha,
        None,
        bind=("_scrim_alpha_transition", "open_progress", "scrim_color"),
    )
    """
    Multiplier for alpha channel of :attr:`scrim_color`. For internal
    usage only.
    """

    scrim_alpha_transition = StringProperty("linear")
    """
    The name of the animation transition type to use for changing
    :attr:`scrim_alpha`.

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

    def _get_scrim_alpha_transition(self):
        return getattr(AnimationTransition, self.scrim_alpha_transition)

    _scrim_alpha_transition = AliasProperty(
        _get_scrim_alpha_transition,
        None,
        bind=("scrim_alpha_transition",),
        cache=True,
    )

    opening_transition = StringProperty("out_cubic")
    """
    The name of the animation transition type to use when animating to
    the :attr:`state` `'open'`.

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

    opening_time = NumericProperty(0.2)
    """
    The time taken for the panel to slide to the :attr:`state` `'open'`.

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

    closing_transition = StringProperty("out_sine")
    """The name of the animation transition type to use when animating to
    the :attr:`state` 'close'.

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

    closing_time = NumericProperty(0.2)
    """
    The time taken for the panel to slide to the :attr:`state` `'close'`.

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

    background_color = ColorProperty(None)
    """
    The drawer background color in (r, g, b, a) or string format.

    .. versionadded:: 2.0.0

    :attr:`background_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    theme_elevation_level = "Custom"
    """
    Drawer elevation level scheme name.

    .. versionadded:: 2.0.0

    Available options are: `'Primary'`, `'Custom'`.

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

    elevation_level = 1
    """
    Drawer elevation level (values from 0 to 5)

    .. versionadded:: 2.2.0

    :attr:`elevation_level` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `2`.
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.register_event_type("on_open")
        self.register_event_type("on_close")
        self.bind(
            open_progress=self.update_status,
            status=self.update_status,
            state=self.update_status,
        )
        Window.bind(on_keyboard=self._handle_keyboard)

    def set_properties_widget(self) -> None:
        pass

    def set_state(self, new_state="toggle", animation=True) -> None:
        """
        Change state of the side panel.
        New_state can be one of `"toggle"`, `"open"` or `"close"`.
        """

        if new_state == "toggle":
            new_state = "close" if self.state == "open" else "open"

        if new_state == "open":
            Animation.cancel_all(self, "open_progress")
            self.status = "opening_with_animation"
            if animation:
                anim = Animation(
                    open_progress=1.0,
                    d=self.opening_time * (1 - self.open_progress),
                    t=self.opening_transition,
                )
                anim.bind(on_complete=self._check_state)
                anim.start(self)
            else:
                self.open_progress = 1
        else:  # "close"
            Animation.cancel_all(self, "open_progress")
            self.status = "closing_with_animation"
            if animation:
                anim = Animation(
                    open_progress=0.0,
                    d=self.closing_time * self.open_progress,
                    t=self.closing_transition,
                )
                anim.bind(on_complete=self._check_state)
                anim.start(self)
            else:
                self.open_progress = 0

    def update_status(self, *args) -> None:
        status = self.status
        if status == "closed":
            self.state = "close"
        elif status == "opened":
            self.state = "open"
        elif self.open_progress == 1 and status == "opening_with_animation":
            self.status = "opened"
            self.state = "open"
        elif self.open_progress == 0 and status == "closing_with_animation":
            self.status = "closed"
            self.state = "close"
        elif status in (
            "opening_with_swipe",
            "opening_with_animation",
            "closing_with_swipe",
            "closing_with_animation",
        ):
            pass
        if self.status == "closed":
            self.opacity = 0
        else:
            self.opacity = 1

    def get_dist_from_side(self, x: float) -> float:
        if self.anchor == "left":
            return 0 if x < 0 else x
        return 0 if x > Window.width else Window.width - x

    def on_touch_down(self, touch):
        if self.status == "closed":
            return False
        elif self.status == "opened":
            for child in self.children[:]:
                if child.dispatch("on_touch_down", touch):
                    return True
        if self.drawer_type == "standard" and not self.collide_point(
            touch.ox, touch.oy
        ):
            return False
        return True

    def on_touch_move(self, touch):
        if self.enable_swiping:
            if self.status == "closed":
                if (
                    self.get_dist_from_side(touch.ox) <= self.swipe_edge_width
                    and abs(touch.x - touch.ox) > self.swipe_distance
                ):
                    self.status = "opening_with_swipe"
            elif self.status == "opened":
                if abs(touch.x - touch.ox) > self.swipe_distance:
                    self.status = "closing_with_swipe"

        if self.status in ("opening_with_swipe", "closing_with_swipe"):
            self.open_progress = max(
                min(
                    self.open_progress
                    + (touch.dx if self.anchor == "left" else -touch.dx)
                    / self.width,
                    1,
                ),
                0,
            )
            return True
        return super().on_touch_move(touch)

    def on_touch_up(self, touch):
        if self.status == "opening_with_swipe":
            if self.open_progress > 0.5:
                self.set_state("open", animation=True)
            else:
                self.set_state("close", animation=True)
        elif self.status == "closing_with_swipe":
            if self.open_progress < 0.5:
                self.set_state("close", animation=True)
            else:
                self.set_state("open", animation=True)
        elif self.status == "opened":
            if self.close_on_click and not self.collide_point(
                touch.ox, touch.oy
            ):
                self.set_state("close", animation=True)
            elif self.drawer_type == "standard" and not self.collide_point(
                touch.ox, touch.oy
            ):
                return False
        elif self.status == "closed":
            return False
        return super().on_touch_up(touch)

    def on_radius(self, instance_navigation_drawer, radius_value: list) -> None:
        """Fired when the :attr:`radius` value changes."""

        self._radius = radius_value

    def on_drawer_type(
        self, instance_navigation_drawer, drawer_type: str
    ) -> None:
        """Fired when the :attr:`drawer_type` value changes."""

        if self.drawer_type == "standard":
            self.enable_swiping = False
            self.close_on_click = False
        else:
            self.enable_swiping = True
            self.close_on_click = True

    def on_open(self, *args) -> None:
        """Fired when the navigation drawer is opened."""

    def on_close(self, *args) -> None:
        """Fired when the navigation drawer is closed."""

    def _handle_keyboard(self, window, key, *largs):
        if key == 27 and self.status == "opened" and self.close_on_click:
            self.set_state("close")
            return True

    def _check_state(self, *args):
        if self.state == "open":
            self.dispatch("on_open")
        elif self.state == "close":
            self.dispatch("on_close")
