"""
Components/Menu
===============

.. seealso::

    `Material Design spec, Menus <https://material.io/components/menus>`_

.. rubric:: Menus display a list of choices on temporary surfaces.

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

Usage
-----

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.menu import MDDropdownMenu

    KV = '''
    Screen:

        MDRaisedButton:
            id: button
            text: "PRESS ME"
            pos_hint: {"center_x": .5, "center_y": .5}
            on_release: app.menu.open()
    '''


    class Test(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)
            menu_items = [{"text": f"Item {i}"} for i in range(5)]
            self.menu = MDDropdownMenu(
                caller=self.screen.ids.button,
                items=menu_items,
                width_mult=4,
            )
            self.menu.bind(on_release=self.menu_callback)

        def menu_callback(self, instance_menu, instance_menu_item):
            print(instance_menu, instance_menu_item)

        def build(self):
            return self.screen


    Test().run()

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

.. Warning:: Do not create the :class:`~MDDropdownMenu` object when you open
    the menu window. Because on a mobile device this one will be very slow!

Wrong
-----

.. code-block:: python

    menu = MDDropdownMenu(caller=self.screen.ids.button, items=menu_items)
    menu.open()

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

Customization of menu item
--------------------------

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

You must create a new class that inherits from the :class:`~RightContent` class:

.. code-block:: python

    class RightContentCls(RightContent):
        pass

Now in the KV rule you can create your own elements that will be displayed in
the menu item on the right:

.. code-block:: kv

    <RightContentCls>
        disabled: True

        MDIconButton:
            icon: root.icon
            user_font_size: "16sp"
            pos_hint: {"center_y": .5}

        MDLabel:
            text: root.text
            font_style: "Caption"
            size_hint_x: None
            width: self.texture_size[0]
            text_size: None, None

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

Now create menu items as usual, but add the key ``right_content_cls`` whose
value is the class ``RightContentCls`` that you created:

.. code-block:: python

    menu_items = [
        {
            "right_content_cls": RightContentCls(
                text=f"R+{i}", icon="apple-keyboard-command",
            ),
            "icon": "git",
            "text": f"Item {i}",
        }
        for i in range(5)
    ]
    self.menu = MDDropdownMenu(
        caller=self.screen.ids.button, items=menu_items, width_mult=4
    )

Full example
------------

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.menu import MDDropdownMenu, RightContent

    KV = '''
    <RightContentCls>
        disabled: True

        MDIconButton:
            icon: root.icon
            user_font_size: "16sp"
            pos_hint: {"center_y": .5}

        MDLabel:
            text: root.text
            font_style: "Caption"
            size_hint_x: None
            width: self.texture_size[0]
            text_size: None, None


    Screen:

        MDRaisedButton:
            id: button
            text: "PRESS ME"
            pos_hint: {"center_x": .5, "center_y": .5}
            on_release: app.menu.open()
    '''


    class RightContentCls(RightContent):
        pass


    class Test(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)
            menu_items = [
                {
                    "right_content_cls": RightContentCls(
                        text=f"R+{i}", icon="apple-keyboard-command",
                    ),
                    "icon": "git",
                    "text": f"Item {i}",
                }
                for i in range(5)
            ]
            self.menu = MDDropdownMenu(
                caller=self.screen.ids.button, items=menu_items, width_mult=4
            )
            self.menu.bind(on_release=self.menu_callback)

        def menu_callback(self, instance_menu, instance_menu_item):
            instance_menu.dismiss()

        def build(self):
            return self.screen


    Test().run()

Menu without icons on the left
------------------------------

If you do not want to use the icons in the menu items on the left,
then do not use the "icon" key when creating menu items:

.. code-block:: python

        menu_items = [
            {
                "right_content_cls": RightContentCls(
                    text=f"R+{i}", icon="apple-keyboard-command",
                ),
                "text": f"Item {i}",
            }
            for i in range(5)
        ]

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-without-icon.png
    :align: center

Item height adjustment
----------------------

.. code-block:: python

        menu_items = [
            {
                "right_content_cls": RightContentCls(
                    text=f"R+{i}", icon="apple-keyboard-command",
                ),
                "text": f"Item {i}",
                "height": "36dp",
                "top_pad": "10dp",
                "bot_pad": "10dp",
            }
            for i in range(5)
        ]

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

Mixin items
-----------

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.menu import MDDropdownMenu, RightContent

    KV = '''
    <RightContentCls>
        disabled: True

        MDIconButton:
            icon: root.icon
            user_font_size: "16sp"
            pos_hint: {"center_y": .5}

        MDLabel:
            text: root.text
            font_style: "Caption"
            size_hint_x: None
            width: self.texture_size[0]
            text_size: None, None


    Screen:

        MDRaisedButton:
            id: button
            text: "PRESS ME"
            pos_hint: {"center_x": .5, "center_y": .5}
            on_release: app.menu.open()
    '''


    class RightContentCls(RightContent):
        pass


    class Test(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)

            menu_items = []
            data = [
                {"": "Open"},
                {},
                {"open-in-app": "Open in app >"},
                {"trash-can-outline": "Move to Trash"},
                {"rename-box": "Rename"},
                {"zip-box-outline": "Create zip"},
                {},
                {"": "Properties"},
            ]

            for data_item in data:
                if data_item:
                    if list(data_item.items())[0][1].endswith(">"):
                        menu_items.append(
                            {
                                "right_content_cls": RightContentCls(
                                    icon="menu-right-outline",
                                ),
                                "icon": list(data_item.items())[0][0],
                                "text": list(data_item.items())[0][1][:-2],
                                "height": "36dp",
                                "top_pad": "10dp",
                                "bot_pad": "10dp",
                                "divider": None,
                            }
                        )
                    else:
                        menu_items.append(
                            {
                                "text": list(data_item.items())[0][1],
                                "icon": list(data_item.items())[0][0],
                                "font_style": "Caption",
                                "height": "36dp",
                                "top_pad": "10dp",
                                "bot_pad": "10dp",
                                "divider": None,
                            }
                        )
                else:
                    menu_items.append(
                        {"viewclass": "MDSeparator", "height": 1}
                    )
            self.menu = MDDropdownMenu(
                caller=self.screen.ids.button,
                items=menu_items,
                width_mult=4,
            )
            self.menu.bind(on_release=self.menu_callback)

        def menu_callback(self, instance_menu, instance_menu_item):
            print(instance_menu, instance_menu_item


        def build(self):
            return self.screen


    Test().run()

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

Hover Behavior
--------------

.. code-block:: python

    self.menu = MDDropdownMenu(
        ...,
        ...,
        selected_color=self.theme_cls.primary_dark_hue,
    )

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-with-hover.gif
    :align: center

Create submenu
--------------

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.menu import MDDropdownMenu

    KV = '''
    Screen:

        MDRaisedButton:
            id: button
            text: "PRESS ME"
            pos_hint: {"center_x": .5, "center_y": .5}
            on_release: app.menu.open()
    '''


    class CustomDrop(MDDropdownMenu):
        def set_bg_color_items(self, instance_selected_item):
            if self.selected_color and not MDApp.get_running_app().submenu:
                for item in self.menu.ids.box.children:
                    if item is not instance_selected_item:
                        item.bg_color = (0, 0, 0, 0)
                    else:
                        instance_selected_item.bg_color = self.selected_color


    class Test(MDApp):
        submenu = None

        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)
            menu_items = [
                {
                    "icon": "git",
                    "text": f"Item {i}" if i != 3 else "Open submenu",
                }
                for i in range(5)
            ]
            self.menu = CustomDrop(
                caller=self.screen.ids.button,
                items=menu_items,
                width_mult=4,
                selected_color=self.theme_cls.bg_darkest
            )
            self.menu.bind(on_enter=self.check_item)

        def check_item(self, menu, item):
            if item.text == "Open submenu" and not self.submenu:
                menu_items = [{"text": f"Item {i}"} for i in range(5)]
                self.submenu = MDDropdownMenu(
                    caller=item,
                    items=menu_items,
                    width_mult=4,
                    selected_color=self.theme_cls.bg_darkest,
                )
                self.submenu.bind(on_dismiss=self.set_state_submenu)
                self.submenu.open()

        def set_state_submenu(self, *args):
            self.submenu = None

        def build(self):
            return self.screen


    Test().run()

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

Menu with MDToolbar
-------------------

.. Warning:: The :class:`~MDDropdownMenu` does not work with the standard
    :class:`~kivymd.uix.toolbar.MDToolbar`. You can use your own
    ``CustomToolbar`` and bind the menu window output to its elements.

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.menu import MDDropdownMenu
    from kivymd.theming import ThemableBehavior
    from kivymd.uix.behaviors import RectangularElevationBehavior
    from kivymd.uix.boxlayout import MDBoxLayout

    KV = '''
    <CustomToolbar>:
        size_hint_y: None
        height: self.theme_cls.standard_increment
        padding: "5dp"
        spacing: "12dp"

        MDIconButton:
            id: button_1
            icon: "menu"
            pos_hint: {"center_y": .5}
            on_release: app.menu_1.open()

        MDLabel:
            text: "MDDropdownMenu"
            pos_hint: {"center_y": .5}
            size_hint_x: None
            width: self.texture_size[0]
            text_size: None, None
            font_style: 'H6'

        Widget:

        MDIconButton:
            id: button_2
            icon: "dots-vertical"
            pos_hint: {"center_y": .5}
            on_release: app.menu_2.open()


    Screen:

        CustomToolbar:
            id: toolbar
            elevation: 10
            pos_hint: {"top": 1}
    '''


    class CustomToolbar(
        ThemableBehavior, RectangularElevationBehavior, MDBoxLayout,
    ):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.md_bg_color = self.theme_cls.primary_color


    class Test(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)
            self.menu_1 = self.create_menu(
                "Button menu", self.screen.ids.toolbar.ids.button_1,
            )
            self.menu_2 = self.create_menu(
                "Button dots", self.screen.ids.toolbar.ids.button_2,
            )

        def create_menu(self, text, instance):
            menu_items = [{"icon": "git", "text": text} for i in range(5)]
            menu = MDDropdownMenu(caller=instance, items=menu_items, width_mult=5)
            menu.bind(on_release=self.menu_callback)
            return menu

        def menu_callback(self, instance_menu, instance_menu_item):
            instance_menu.dismiss()

        def build(self):
            return self.screen


    Test().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-with-toolbar.gif
    :align: center

Position menu
=============

Bottom position
---------------

.. seealso::

    :attr:`~MDDropdownMenu.position`

.. code-block:: python

    from kivy.clock import Clock
    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.menu import MDDropdownMenu

    KV = '''
    Screen

        MDTextField:
            id: field
            pos_hint: {'center_x': .5, 'center_y': .5}
            size_hint_x: None
            width: "200dp"
            hint_text: "Password"
            on_focus: if self.focus: app.menu.open()
    '''


    class Test(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)
            menu_items = [{"icon": "git", "text": f"Item {i}"} for i in range(5)]
            self.menu = MDDropdownMenu(
                caller=self.screen.ids.field,
                items=menu_items,
                position="bottom",
                width_mult=4,
            )
            self.menu.bind(on_release=self.set_item)

        def set_item(self, instance_menu, instance_menu_item):
            def set_item(interval):
                self.screen.ids.field.text = instance_menu_item.text
                instance_menu.dismiss()
            Clock.schedule_once(set_item, 0.5)

        def build(self):
            return self.screen


    Test().run()

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

Center position
---------------

.. code-block:: python

    from kivy.lang import Builder

    from kivymd.app import MDApp
    from kivymd.uix.menu import MDDropdownMenu

    KV = '''
    Screen

        MDDropDownItem:
            id: drop_item
            pos_hint: {'center_x': .5, 'center_y': .5}
            text: 'Item 0'
            on_release: app.menu.open()
    '''


    class Test(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_string(KV)
            menu_items = [{"icon": "git", "text": f"Item {i}"} for i in range(5)]
            self.menu = MDDropdownMenu(
                caller=self.screen.ids.drop_item,
                items=menu_items,
                position="center",
                width_mult=4,
            )
            self.menu.bind(on_release=self.set_item)

        def set_item(self, instance_menu, instance_menu_item):
            self.screen.ids.drop_item.set_item(instance_menu_item.text)
            self.menu.dismiss()

        def build(self):
            return self.screen


    Test().run()

.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-position-center.gif
    :align: center
"""

__all__ = ("MDDropdownMenu", "RightContent")

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 (
    ListProperty,
    NumericProperty,
    ObjectProperty,
    OptionProperty,
    StringProperty,
)
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView

import kivymd.material_resources as m_res
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import HoverBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.list import (
    IRightBodyTouch,
    OneLineAvatarIconListItem,
    OneLineListItem,
    OneLineRightIconListItem,
)

Builder.load_string(
    """
#:import STD_INC kivymd.material_resources.STANDARD_INCREMENT


<RightContent>
    adaptive_width: True


<MDMenuItemIcon>

    IconLeftWidget:
        id: icon_widget
        icon: root.icon


<MDMenu>
    size_hint: None, None
    width: root.width_mult * STD_INC
    bar_width: 0

    MDGridLayout:
        id: box
        cols: 1
        adaptive_height: True


<MDDropdownMenu>

    MDCard:
        id: card
        elevation: 10
        size_hint: None, None
        size: md_menu.size
        pos: md_menu.pos
        md_bg_color: 0, 0, 0, 0
        opacity: md_menu.opacity

        canvas:
            Clear
            Color:
                rgba: root.background_color if root.background_color else root.theme_cls.bg_dark
            RoundedRectangle:
                size: self.size
                pos: self.pos
                radius: [root.radius,]

        MDMenu:
            id: md_menu
            drop_cls: root
            width_mult: root.width_mult
            size_hint: None, None
            size: 0, 0
            opacity: 0
"""
)


class RightContent(IRightBodyTouch, MDBoxLayout):
    text = StringProperty()
    """
    Text item.

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

    icon = StringProperty()
    """
    Icon item.

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


class MDMenuItemBase(HoverBehavior):
    """
    Base class for MenuItem
    """

    def on_enter(self):
        self.parent.parent.drop_cls.set_bg_color_items(self)
        self.parent.parent.drop_cls.dispatch("on_enter", self)

    def on_leave(self):
        self.parent.parent.drop_cls.dispatch("on_leave", self)


class MDMenuItemIcon(MDMenuItemBase, OneLineAvatarIconListItem):
    icon = StringProperty()
    """
    Icon item.

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


class MDMenuItem(MDMenuItemBase, OneLineListItem):

    pass


class MDMenuItemRight(MDMenuItemBase, OneLineRightIconListItem):

    pass


class MDMenu(ScrollView):
    width_mult = NumericProperty(1)
    """
    See :attr:`~MDDropdownMenu.width_mult`.
    """

    drop_cls = ObjectProperty()
    """
    See :class:`~MDDropdownMenu` class.
    """


class MDDropdownMenu(ThemableBehavior, FloatLayout):
    """
    :Events:
        :attr:`on_enter`
            Call when mouse enter the bbox of item menu.
        :attr:`on_leave`
            Call when the mouse exit the item menu.
        :attr:`on_dismiss`
            Call when closes menu.
        :attr:`on_release`
            The method that will be called when you click menu items.
    """

    selected_color = ListProperty()
    """Custom color (``rgba`` format) for list item when hover behavior occurs.

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

    items = ListProperty()
    """
    See :attr:`~kivy.uix.recycleview.RecycleView.data`.

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

    width_mult = NumericProperty(1)
    """
    This number multiplied by the standard increment (56dp on mobile,
    64dp on desktop, determines the width of the menu items.

    If the resulting number were to be too big for the application Window,
    the multiplier will be adjusted for the biggest possible one.

    :attr:`width_mult` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `1`.
    """

    max_height = NumericProperty()
    """
    The menu will grow no bigger than this number. Set to 0 for no limit.

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

    border_margin = NumericProperty("4dp")
    """
    Margin between Window border and menu.

    :attr:`border_margin` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `4dp`.
    """

    ver_growth = OptionProperty(None, allownone=True, options=["up", "down"])
    """
    Where the menu will grow vertically to when opening. Set to None to let
    the widget pick for you. Available options are: `'up'`, `'down'`.

    :attr:`ver_growth` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `None`.
    """

    hor_growth = OptionProperty(None, allownone=True, options=["left", "right"])
    """
    Where the menu will grow horizontally to when opening. Set to None to let
    the widget pick for you. Available options are: `'left'`, `'right'`.

    :attr:`hor_growth` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `None`.
    """

    background_color = ListProperty()
    """
    Color of the background of the menu.

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

    opening_transition = StringProperty("out_cubic")
    """
    Type of animation for opening a menu window.

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

    opening_time = NumericProperty(0.2)
    """
    Menu window opening animation time.

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

    caller = ObjectProperty()
    """
    The widget object that caller the menu window.

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

    position = OptionProperty("auto", options=["auto", "center", "bottom"])
    """
    Menu window position relative to parent element.
    Available options are: `'auto'`, `'center'`, `'bottom'`.

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

    radius = NumericProperty(7)
    """
    Menu radius.

    :attr:`radius` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `'7'`.
    """

    _start_coords = []
    _calculate_complete = False
    _calculate_process = False

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Window.bind(on_resize=self.check_position_caller)
        Window.bind(on_maximize=self.set_menu_properties)
        Window.bind(on_restore=self.set_menu_properties)
        self.register_event_type("on_dismiss")
        self.register_event_type("on_enter")
        self.register_event_type("on_leave")
        self.register_event_type("on_release")
        self.menu = self.ids.md_menu
        self.target_height = 0

    def check_position_caller(self, instance, width, height):
        self.set_menu_properties(0)

    def set_bg_color_items(self, instance_selected_item):
        """Called when a Hover Behavior event occurs for a list item.

        :type instance_selected_item: <kivymd.uix.menu.MDMenuItemIcon object>
        """

        if self.selected_color:
            for item in self.menu.ids.box.children:
                if item is not instance_selected_item:
                    item.bg_color = (0, 0, 0, 0)
                else:
                    instance_selected_item.bg_color = self.selected_color

    def create_menu_items(self):
        """Creates menu items."""

        for data in self.items:
            if data.get("icon") and data.get("right_content_cls", None):

                item = MDMenuItemIcon(
                    text=data.get("text", ""),
                    divider=data.get("divider", "Full"),
                    _txt_top_pad=data.get("top_pad", "20dp"),
                    _txt_bot_pad=data.get("bot_pad", "20dp"),
                )

            elif data.get("icon"):
                item = MDMenuItemIcon(
                    text=data.get("text", ""),
                    divider=data.get("divider", "Full"),
                    _txt_top_pad=data.get("top_pad", "20dp"),
                    _txt_bot_pad=data.get("bot_pad", "20dp"),
                )

            elif data.get("right_content_cls", None):
                item = MDMenuItemRight(
                    text=data.get("text", ""),
                    divider=data.get("divider", "Full"),
                    _txt_top_pad=data.get("top_pad", "20dp"),
                    _txt_bot_pad=data.get("bot_pad", "20dp"),
                )

            else:

                item = MDMenuItem(
                    text=data.get("text", ""),
                    divider=data.get("divider", "Full"),
                    _txt_top_pad=data.get("top_pad", "20dp"),
                    _txt_bot_pad=data.get("bot_pad", "20dp"),
                )

            # Set height item.
            if data.get("height", ""):
                item.height = data.get("height")
            # Compensate icon area by some left padding.
            if not data.get("icon"):
                item._txt_left_pad = data.get("left_pad", "32dp")
            # Set left icon.
            else:
                item.icon = data.get("icon", "")
            item.bind(on_release=lambda x=item: self.dispatch("on_release", x))
            right_content_cls = data.get("right_content_cls", None)
            # Set right content.
            if isinstance(right_content_cls, RightContent):
                item.ids._right_container.width = right_content_cls.width + dp(
                    20
                )
                item.ids._right_container.padding = ("10dp", 0, 0, 0)
                item.add_widget(right_content_cls)
            else:
                if "_right_container" in item.ids:
                    item.ids._right_container.width = 0
            self.menu.ids.box.add_widget(item)

    def set_menu_properties(self, interval=0):
        """Sets the size and position for the menu window."""

        if self.caller:
            if not self.menu.ids.box.children:
                self.create_menu_items()
            # We need to pick a starting point, see how big we need to be,
            # and where to grow to.
            self._start_coords = self.caller.to_window(
                self.caller.center_x, self.caller.center_y
            )
            self.target_width = self.width_mult * m_res.STANDARD_INCREMENT

            # If we're wider than the Window...
            if self.target_width > Window.width:
                # ...reduce our multiplier to max allowed.
                self.target_width = (
                    int(Window.width / m_res.STANDARD_INCREMENT)
                    * m_res.STANDARD_INCREMENT
                )

            # Set the target_height of the menu depending on the size of
            # each MDMenuItem or MDMenuItemIcon
            self.target_height = 0
            for item in self.menu.ids.box.children:
                self.target_height += item.height

            # If we're over max_height...
            if 0 < self.max_height < self.target_height:
                self.target_height = self.max_height

            # Establish vertical growth direction.
            if self.ver_growth is not None:
                ver_growth = self.ver_growth
            else:
                # If there's enough space below us:
                if (
                    self.target_height
                    <= self._start_coords[1] - self.border_margin
                ):
                    ver_growth = "down"
                # if there's enough space above us:
                elif (
                    self.target_height
                    < Window.height - self._start_coords[1] - self.border_margin
                ):
                    ver_growth = "up"
                # Otherwise, let's pick the one with more space and adjust ourselves.
                else:
                    # If there"s more space below us:
                    if (
                        self._start_coords[1]
                        >= Window.height - self._start_coords[1]
                    ):
                        ver_growth = "down"
                        self.target_height = (
                            self._start_coords[1] - self.border_margin
                        )
                    # If there's more space above us:
                    else:
                        ver_growth = "up"
                        self.target_height = (
                            Window.height
                            - self._start_coords[1]
                            - self.border_margin
                        )

            if self.hor_growth is not None:
                hor_growth = self.hor_growth
            else:
                # If there's enough space to the right:
                if (
                    self.target_width
                    <= Window.width - self._start_coords[0] - self.border_margin
                ):
                    hor_growth = "right"
                # if there's enough space to the left:
                elif (
                    self.target_width
                    < self._start_coords[0] - self.border_margin
                ):
                    hor_growth = "left"
                # Otherwise, let's pick the one with more space and adjust ourselves.
                else:
                    # if there"s more space to the right:
                    if (
                        Window.width - self._start_coords[0]
                        >= self._start_coords[0]
                    ):
                        hor_growth = "right"
                        self.target_width = (
                            Window.width
                            - self._start_coords[0]
                            - self.border_margin
                        )
                    # if there"s more space to the left:
                    else:
                        hor_growth = "left"
                        self.target_width = (
                            self._start_coords[0] - self.border_margin
                        )

            if ver_growth == "down":
                self.tar_y = self._start_coords[1] - self.target_height
            else:  # should always be "up"
                self.tar_y = self._start_coords[1]

            if hor_growth == "right":
                self.tar_x = self._start_coords[0]
            else:  # should always be "left"
                self.tar_x = self._start_coords[0] - self.target_width
            self._calculate_complete = True

    def open(self):
        """Animate the opening of a menu window."""

        def open(interval):
            if not self._calculate_complete:
                return
            if self.position == "auto":
                self.menu.pos = self._start_coords
                anim = Animation(
                    x=self.tar_x,
                    y=self.tar_y,
                    width=self.target_width,
                    height=self.target_height,
                    duration=self.opening_time,
                    opacity=1,
                    transition=self.opening_transition,
                )
                anim.start(self.menu)
            else:
                if self.position == "center":
                    self.menu.pos = (
                        self._start_coords[0] - self.target_width / 2,
                        self._start_coords[1] - self.target_height / 2,
                    )
                elif self.position == "bottom":
                    self.menu.pos = (
                        self._start_coords[0] - self.target_width / 2,
                        self.caller.pos[1] - self.target_height,
                    )
                anim = Animation(
                    width=self.target_width,
                    height=self.target_height,
                    duration=self.opening_time,
                    opacity=1,
                    transition=self.opening_transition,
                )
            anim.start(self.menu)
            Window.add_widget(self)
            Clock.unschedule(open)
            self._calculate_process = False

        self.set_menu_properties()
        if not self._calculate_process:
            self._calculate_process = True
            Clock.schedule_interval(open, 0)

    def on_touch_down(self, touch):
        if not self.menu.collide_point(*touch.pos):
            self.dispatch("on_dismiss")
            return True
        super().on_touch_down(touch)
        return True

    def on_touch_move(self, touch):
        super().on_touch_move(touch)
        return True

    def on_touch_up(self, touch):
        super().on_touch_up(touch)
        return True

    def on_enter(self, instance):
        """Call when mouse enter the bbox of the item of menu."""

    def on_leave(self, instance):
        """Call when the mouse exit the item of menu."""

    def on_release(self, *args):
        """The method that will be called when you click menu items."""

    def on_dismiss(self):
        """Called when the menu is closed."""

        Window.remove_widget(self)
        self.menu.width = 0
        self.menu.height = 0
        self.menu.opacity = 0

    def dismiss(self):
        """Closes the menu."""

        self.on_dismiss()
