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

.. seealso::

    `Material Design 2 spec, Navigation drawer <https://material.io/components/navigation-drawer>`_ and
    `Material Design 3 spec, 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

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

Anatomy
-------

.. code-block:: kv

    Root:

        MDNavigationLayout:

            MDScreenManager:

                Screen_1:

                Screen_2:

            MDNavigationDrawer:

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

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

.. code-block:: python

    from kivy.lang import Builder

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

    KV = '''
    MDScreen:

        MDNavigationLayout:

            MDScreenManager:

                MDScreen:

                    MDTopAppBar:
                        title: "Navigation Drawer"
                        elevation: 10
                        pos_hint: {"top": 1}
                        md_bg_color: "#e7e4c0"
                        specific_text_color: "#4a4939"
                        left_action_items:
                            [['menu', lambda x: nav_drawer.set_state("open")]]


            MDNavigationDrawer:
                id: nav_drawer
                md_bg_color: "#f7f4e7"

                ContentNavigationDrawer:
    '''


    class ContentNavigationDrawer(MDBoxLayout):
        pass


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


    TestNavigationDrawer().run()

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

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

Custom content for navigation drawer
------------------------------------

Let's extend the ``ContentNavigationDrawer`` class from the above example and
create content for our :class:`~MDNavigationDrawer` panel:

.. code-block:: kv

    # Menu item in the DrawerList list.
    <ItemDrawer>
        theme_text_color: "Custom"
        on_release: self.parent.set_color_item(self)

        IconLeftWidget:
            id: icon
            icon: root.icon
            theme_text_color: "Custom"
            text_color: root.text_color

.. code-block:: python

    class ItemDrawer(OneLineIconListItem):
        icon = StringProperty()

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

Top of ``ContentNavigationDrawer`` and ``DrawerList`` for menu items:

.. code-block:: kv

    <ContentNavigationDrawer>
        orientation: "vertical"
        padding: "8dp"
        spacing: "8dp"

        AnchorLayout:
            anchor_x: "left"
            size_hint_y: None
            height: avatar.height

            Image:
                id: avatar
                size_hint: None, None
                size: "56dp", "56dp"
                source: "kivymd.png"

        MDLabel:
            text: "KivyMD library"
            font_style: "Button"
            size_hint_y: None
            height: self.texture_size[1]

        MDLabel:
            text: "kivydevelopment@gmail.com"
            font_style: "Caption"
            size_hint_y: None
            height: self.texture_size[1]

        ScrollView:

            DrawerList:
                id: md_list

.. code-block:: python

    class ContentNavigationDrawer(BoxLayout):
        pass


    class DrawerList(ThemableBehavior, MDList):
        def set_color_item(self, instance_item):
            '''Called when tap on a menu item.'''

            # Set the color of the icon and text for the menu item.
            for item in self.children:
                if item.text_color == self.theme_cls.primary_color:
                    item.text_color = self.theme_cls.text_color
                    break
            instance_item.text_color = self.theme_cls.primary_color

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

Create a menu list for ``ContentNavigationDrawer``:

.. code-block:: python

    def on_start(self):
        icons_item = {
            "folder": "My files",
            "account-multiple": "Shared with me",
            "star": "Starred",
            "history": "Recent",
            "checkbox-marked": "Shared with me",
            "upload": "Upload",
        }
        for icon_name in icons_item.keys():
            self.root.ids.content_drawer.ids.md_list.add_widget(
                ItemDrawer(icon=icon_name, text=icons_item[icon_name])
            )

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

Standard content for the navigation bar
---------------------------------------

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp

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


    <DrawerLabelItem@MDNavigationDrawerItem>
        bg_color: "#f7f4e7"
        text_color: "#4a4939"
        icon_color: "#4a4939"
        _no_ripple_effect: True


    MDScreen:

        MDNavigationLayout:

            MDScreenManager:

                MDScreen:

                    MDTopAppBar:
                        title: "Navigation Drawer"
                        elevation: 10
                        pos_hint: {"top": 1}
                        md_bg_color: "#e7e4c0"
                        specific_text_color: "#4a4939"
                        left_action_items:
                            [ \
                            [ \
                            'menu', lambda x: \
                            nav_drawer.set_state("open") \
                            if nav_drawer.state == "close" else \
                            nav_drawer.set_state("close") \
                            ] \
                            ]

            MDNavigationDrawer:
                id: nav_drawer
                radius: (0, 16, 16, 0) if self.anchor == "left" else (16, 0, 0, 16)
                md_bg_color: "#f7f4e7"

                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 TestNavigationDrawer(MDApp):
        def build(self):
            self.theme_cls.primary_palette = "Indigo"
            return Builder.load_string(KV)


    TestNavigationDrawer().run()

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

Switching screens in the ``ScreenManager`` and using the common ``MDTopAppBar``
-----------------------------------------------------------------------------

.. code-block:: python

    from kivy.lang import Builder
    from kivy.properties import ObjectProperty

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

    KV = '''
    <ContentNavigationDrawer>

        ScrollView:

            MDList:

                OneLineListItem:
                    text: "Screen 1"
                    on_press:
                        root.nav_drawer.set_state("close")
                        root.screen_manager.current = "scr 1"

                OneLineListItem:
                    text: "Screen 2"
                    on_press:
                        root.nav_drawer.set_state("close")
                        root.screen_manager.current = "scr 2"


    MDScreen:

        MDTopAppBar:
            id: toolbar
            pos_hint: {"top": 1}
            elevation: 10
            title: "MDNavigationDrawer"
            left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]

        MDNavigationLayout:
            x: toolbar.height

            MDScreenManager:
                id: screen_manager

                MDScreen:
                    name: "scr 1"

                    MDLabel:
                        text: "Screen 1"
                        halign: "center"

                MDScreen:
                    name: "scr 2"

                    MDLabel:
                        text: "Screen 2"
                        halign: "center"

            MDNavigationDrawer:
                id: nav_drawer

                ContentNavigationDrawer:
                    screen_manager: screen_manager
                    nav_drawer: nav_drawer
    '''


    class ContentNavigationDrawer(MDBoxLayout):
        screen_manager = ObjectProperty()
        nav_drawer = ObjectProperty()


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


    TestNavigationDrawer().run()
"""

__all__ = (
    "MDNavigationLayout",
    "MDNavigationDrawer",
    "MDNavigationDrawerItem",
    "MDNavigationDrawerMenu",
    "MDNavigationDrawerHeader",
    "MDNavigationDrawerLabel",
    "MDNavigationDrawerDivider",
)

import os
from typing import Union

from kivy.animation import Animation, AnimationTransition
from kivy.clock import Clock
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.properties import (
    AliasProperty,
    BooleanProperty,
    ColorProperty,
    NumericProperty,
    ObjectProperty,
    OptionProperty,
    StringProperty,
    VariableListProperty,
)
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager

from kivymd import uix_path
from kivymd.uix.behaviors import (
    DeclarativeBehavior,
    FakeRectangularElevationBehavior,
)
from kivymd.uix.behaviors.focus_behavior import FocusBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.card import MDCard
from kivymd.uix.list import MDList, OneLineAvatarIconListItem
from kivymd.uix.scrollview import MDScrollView
from kivymd.uix.toolbar import MDTopAppBar

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 MDNavigationLayout(DeclarativeBehavior, FloatLayout):
    _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.type == "standard":
            manager.size_hint_x = None
            if drawer.anchor == "left":
                manager.x = drawer.width + drawer.x
                manager.width = self.width - manager.x
            else:
                manager.x = 0
                manager.width = drawer.x
        elif 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(MDBoxLayout):
    """
    Implements a label for a menu for :class:`~MDNavigationDrawer` class.

    .. versionadded:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

                MDNavigationDrawerLabel:
                    text: "Mail"

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

    text = StringProperty()
    """
    Text label.

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

    padding = VariableListProperty(["20dp", 0, 0, "8dp"])
    """
    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].

    :attr:`padding` is a :class:`~kivy.properties.VariableListProperty`
    and defaults to `['20dp', 0, 0, '8dp']`.
    """


class MDNavigationDrawerDivider(MDBoxLayout):
    """
    Implements a divider for a menu for :class:`~MDNavigationDrawer` class.

    .. versionadded:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

                MDNavigationDrawerLabel:
                    text: "Mail"

                MDNavigationDrawerDivider:

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

    padding = VariableListProperty(["20dp", "12dp", 0, "12dp"])
    """
    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].

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

    color = ColorProperty(None)
    """
    Divider color in ``rgba`` format.

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


class MDNavigationDrawerHeader(MDBoxLayout):
    """
    Implements a header for a menu for :class:`~MDNavigationDrawer` class.

    .. versionadded:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

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

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

    source = StringProperty()
    """
    Image logo path.

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

                MDNavigationDrawerHeader:
                    title: "Header title"
                    text: "Header text"
                    source: "logo.png"
                    spacing: "4dp"
                    padding: "12dp", 0, 0, "56dp"

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

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

    title = StringProperty()
    """
    Title shown in the first line.

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

    title_halign = StringProperty("left")
    """
    Title halign first line.

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

    title_color = ColorProperty(None)
    """
    Title text color.

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

    title_font_style = StringProperty("H4")
    """
    Title shown in the first line.

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

    title_font_size = StringProperty("34sp")
    """
    Title shown in the first line.

    :attr:`title_font_size` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'34sp'`.
    """

    text = StringProperty()
    """
    Text shown in the second line.

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

    text_halign = StringProperty("left")
    """
    Text halign first line.

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

    text_color = ColorProperty(None)
    """
    Title text color.

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

    text_font_style = StringProperty("H6")
    """
    Title shown in the first line.

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

    text_font_size = StringProperty("20sp")
    """
    Title shown in the first line.

    :attr:`text_font_size` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'20sp'`.
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self.check_content)

    def check_content(self, interval: Union[int, float]) -> None:
        """Removes widgets that the user has not added to the container."""

        if not self.title:
            self.ids.label_box.remove_widget(self.ids.title)
        if not self.text:
            self.ids.label_box.remove_widget(self.ids.text)
        if not self.source:
            self.remove_widget(self.ids.logo)


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

    .. versionadded:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

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

                MDNavigationDrawerItem
                    icon: "gmail"
                    right_text: "+99"
                    text: "Inbox"

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

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

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

    icon = StringProperty()
    """
    Icon item.

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

    icon_color = ColorProperty(None)
    """
    Icon color item.

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

    selected_color = ColorProperty([0, 0, 0, 1])
    """
    The color of the icon and text of the selected item.

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

    right_text = StringProperty()
    """
    Right text item.

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

    text_right_color = ColorProperty(None)
    """
    Right text color item.

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

    _text_color = None
    _text_right_color = None
    # kivymd.uix.navigationdrawer.navigationdrawer.MDNavigationDrawerMenu
    _drawer_menu = ObjectProperty()


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

    .. 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, MDList):
            return super().add_widget(widget, *args, **kwargs)
        else:
            if isinstance(widget, MDNavigationDrawerItem):
                widget._drawer_menu = self
            self.ids.menu.add_widget(widget)

    def reset_active_color(self, item: MDNavigationDrawerItem) -> None:
        for widget in self.ids.menu.children:
            if issubclass(widget.__class__, MDNavigationDrawerItem):
                if widget != item:
                    widget.selected = False
                else:
                    widget.selected = True

            if (
                issubclass(widget.__class__, MDNavigationDrawerItem)
                and widget != item
            ):
                if widget._text_color:
                    widget.text_color = widget._text_color


class MDNavigationDrawer(
    DeclarativeBehavior, MDCard, FakeRectangularElevationBehavior
):
    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.

    Standard
    --------

    .. code-block:: kv

        MDNavigationDrawer:
            type: "standard"

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

    Model
    -----

    .. code-block:: kv

        MDNavigationDrawer:
            type: "modal"

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

    :attr:`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'`.

    Left
    ----

    .. code-block:: kv

        MDNavigationDrawer:
            anchor: "left"

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

    Right
    -----

    .. code-block:: kv

        MDNavigationDrawer:
            anchor: "right"

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

    :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. Alpha channel will be multiplied with
    :attr:`_scrim_alpha`. Set fourth channel to 0 if you want to disable
    scrim.

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

    .. code-block:: kv

        MDNavigationDrawer:
            scrim_color: 0, 0, 0, .8
            # scrim_color: 0, 0, 0, .2

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

    padding = VariableListProperty([16, 16, 12, 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: 56, 56, 12, 16

    .. 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 '[16, 16, 12, 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.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`.
    """

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

    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:
                Animation(
                    open_progress=1.0,
                    d=self.opening_time * (1 - self.open_progress),
                    t=self.opening_transition,
                ).start(self)
            else:
                self.open_progress = 1
        else:  # "close"
            Animation.cancel_all(self, "open_progress")
            self.status = "closing_with_animation"
            if animation:
                Animation(
                    open_progress=0.0,
                    d=self.closing_time * self.open_progress,
                    t=self.closing_transition,
                ).start(self)
            else:
                self.open_progress = 0

    def update_status(self, *_) -> 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.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.type == "standard" and not self.collide_point(
                touch.ox, touch.oy
            ):
                return False
        elif self.status == "closed":
            return False
        return True

    def on_radius(self, instance_navigation_drawer, radius_value: list) -> None:
        self._radius = radius_value

    def on_type(self, instance_navigation_drawer, drawer_type: str) -> None:
        if self.type == "standard":
            self.enable_swiping = False
            self.close_on_click = False
        else:
            self.enable_swiping = True
            self.close_on_click = True

    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
