1291 lines
38 KiB
Python
1291 lines
38 KiB
Python
"""
|
|
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")
|