""" Components/NavigationDrawer =========================== .. seealso:: `Material Design 2 spec, Navigation drawer `_ and `Material Design 3 spec, Navigation drawer `_ .. 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: ScreenManager: 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: ScreenManager: 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. 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 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 = ''' focus_color: "#e7e4c0" unfocus_color: "#f7f4e7" text_color: "#4a4939" icon_color: "#4a4939" ripple_color: "#c5bdd2" selected_color: "#0c6c4d" bg_color: "#f7f4e7" text_color: "#4a4939" icon_color: "#4a4939" _no_ripple_effect: True MDScreen: MDNavigationLayout: ScreenManager: 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" title_color: "#4a4939" 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 = ''' 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 ScreenManager: 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 kivy.uix.scrollview import ScrollView from kivymd import uix_path from kivymd.uix.behaviors import FakeRectangularElevationBehavior, FocusBehavior from kivymd.uix.boxlayout import MDBoxLayout from kivymd.uix.card import MDCard from kivymd.uix.list import MDList, OneLineAvatarIconListItem 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(FloatLayout): _scrim_color = ObjectProperty(None) _scrim_rectangle = ObjectProperty(None) _screen_manager = ObjectProperty(None) _navigation_drawer = ObjectProperty(None) def __init__(self, **kwargs): super().__init__(**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(ScrollView): """ 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(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, **kwargs): super().__init__(**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