Sideband/sbapp/kivymd/uix/navigationrail/navigationrail.py
2022-07-07 22:16:10 +02:00

991 lines
28 KiB
Python

"""
Components/NavigationRail
=========================
.. versionadded:: 1.0.0
.. seealso::
`Material Design spec, Navigation rail <https://m3.material.io/components/navigation-rail/specs>`_
.. rubric::
Navigation rails provide access to primary destinations in apps when using
tablet and desktop screens.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail.png
:align: center
Usage
=====
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
MDNavigationRailItem:
MDNavigationRailItem:
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDBoxLayout:
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
MDNavigationRailItem:
text: "JavaScript"
icon: "language-javascript"
MDNavigationRailItem:
text: "CPP"
icon: "language-cpp"
MDNavigationRailItem:
text: "Git"
icon: "git"
MDScreen:
'''
class Example(MDApp):
def build(self):
return Builder.load_string(KV)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-usage.png
:align: center
Example
=======
.. code-block:: python
from kivy.clock import Clock
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFillRoundFlatIconButton
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
KV = '''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<ExtendedButton>
elevation: 3
-height: "56dp"
<DrawerClickableItem@MDNavigationDrawerItem>
focus_color: "#e7e4c0"
unfocus_color: "#fffcf4"
MDScreen:
MDNavigationLayout:
ScreenManager:
MDScreen:
MDBoxLayout:
orientation: "vertical"
MDBoxLayout:
adaptive_height: True
md_bg_color: "#fffcf4"
padding: "12dp"
MDLabel:
text: "12:00"
adaptive_height: True
pos_hint: {"center_y": .5}
MDBoxLayout:
MDNavigationRail:
id: navigation_rail
md_bg_color: "#fffcf4"
selected_color_background: "#e7e4c0"
ripple_color_item: "#e7e4c0"
on_item_release: app.switch_screen(*args)
MDNavigationRailMenuButton:
on_release: nav_drawer.set_state("open")
MDNavigationRailFabButton:
md_bg_color: "#b0f0d6"
MDNavigationRailItem:
text: "Python"
icon: "language-python"
MDNavigationRailItem:
text: "JavaScript"
icon: "language-javascript"
MDNavigationRailItem:
text: "CPP"
icon: "language-cpp"
MDNavigationRailItem:
text: "Swift"
icon: "language-swift"
ScreenManager:
id: screen_manager
transition:
FadeTransition(duration=.2, clearcolor=app.theme_cls.bg_dark)
MDNavigationDrawer:
id: nav_drawer
radius: (0, 16, 16, 0)
md_bg_color: "#fffcf4"
elevation: 12
width: "240dp"
MDNavigationDrawerMenu:
MDBoxLayout:
orientation: "vertical"
adaptive_height: True
spacing: "12dp"
padding: 0, 0, 0, "12dp"
MDIconButton:
icon: "menu"
ExtendedButton:
text: "Compose"
icon: "pencil"
DrawerClickableItem:
text: "Python"
icon: "language-python"
DrawerClickableItem:
text: "JavaScript"
icon: "language-javascript"
DrawerClickableItem:
text: "CPP"
icon: "language-cpp"
DrawerClickableItem:
text: "Swift"
icon: "language-swift"
'''
class ExtendedButton(
RoundedRectangularElevationBehavior, MDFillRoundFlatIconButton
):
'''
Implements a button of type
`Extended FAB <https://m3.material.io/components/extended-fab/overview>`_.
.. rubric::
Extended FABs help people take primary actions.
They're wider than FABs to accommodate a text label and larger target
area.
This type of buttons is not yet implemented in the standard widget set
of the KivyMD library, so we will implement it ourselves in this class.
'''
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.padding = "16dp"
Clock.schedule_once(self.set_spacing)
def set_spacing(self, interval):
self.ids.box.spacing = "12dp"
def set_radius(self, *args):
if self.rounded_button:
self._radius = self.radius = self.height / 4
class Example(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def switch_screen(
self, instance_navigation_rail, instance_navigation_rail_item
):
'''
Called when tapping on rail menu items. Switches application screens.
'''
self.root.ids.screen_manager.current = (
instance_navigation_rail_item.icon.split("-")[1].lower()
)
def on_start(self):
'''Creates application screens.'''
navigation_rail_items = self.root.ids.navigation_rail.get_items()[:]
navigation_rail_items.reverse()
for widget in navigation_rail_items:
name_screen = widget.icon.split("-")[1].lower()
screen = MDScreen(
name=name_screen,
md_bg_color="#edd769",
radius=[18, 0, 0, 0],
)
box = MDBoxLayout(padding="12dp")
label = MDLabel(
text=name_screen.capitalize(),
font_style="H1",
halign="right",
adaptive_height=True,
shorten=True,
)
box.add_widget(label)
screen.add_widget(box)
self.root.ids.screen_manager.add_widget(screen)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-example.gif
:align: center
"""
__all__ = (
"MDNavigationRail",
"MDNavigationRailFabButton",
"MDNavigationRailMenuButton",
)
import os
from typing import Union
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.logger import Logger
from kivy.metrics import dp
from kivy.properties import (
BooleanProperty,
ColorProperty,
ListProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
VariableListProperty,
)
from kivy.uix.behaviors import ButtonBehavior
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFloatingActionButton, MDIconButton
from kivymd.uix.card import MDCard
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.templates import ScaleWidget
from kivymd.uix.widget import MDWidget
with open(
os.path.join(uix_path, "navigationrail", "navigationrail.kv"),
encoding="utf-8",
) as kv_file:
Builder.load_string(kv_file.read())
class PanelRoot(MDFloatLayout):
"""
Contains
:class:`~MDNavigationRailFabButton`, :class:`~MDNavigationRailMenuButton`
buttons and a :class:`~Paneltems` container with menu items.
"""
class PanelItems(MDBoxLayout):
"""Box for menu items."""
class RippleWidget(MDWidget, ScaleWidget):
"""
Implements a background color for a menu item -
(:class:`~MDNavigationRailItem`).
"""
class MDNavigationRailFabButton(MDFloatingActionButton):
"""Implements an optional floating action button (FAB)."""
icon = StringProperty("pencil")
"""
Button icon name.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailFabButton:
icon: "home"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-fab-button-icon.png
:align: center
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `'pencil'`.
"""
class MDNavigationRailMenuButton(MDIconButton):
"""Implements a menu button."""
icon = StringProperty("menu")
"""
Button icon name.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailMenuButton:
icon: "home"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-menu-button-icon.png
:align: center
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `'menu'`.
"""
class MDNavigationRailItem(ThemableBehavior, ButtonBehavior, MDBoxLayout):
"""Implements a menu item with an icon and text."""
navigation_rail = ObjectProperty()
"""
:class:`~MDNavigationRail` object.
:attr:`navigation_rail` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
icon = StringProperty("checkbox-blank-circle")
"""
Icon item.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
icon: "language-python"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-item-icon.png
:align: center
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `'checkbox-blank'`.
"""
text = StringProperty()
"""
Text item.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-item-text.png
:align: center
:attr:`text` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
badge_icon = StringProperty()
"""
Badge icon name.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
badge_icon: "plus"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-item-badge-icon.png
:align: center
:attr:`badge_icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
badge_icon_color = ColorProperty(None)
"""
Badge icon color in (r, g, b, a) format.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
badge_icon: "plus"
badge_icon_color: 0, 0, 1, 1
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-item-badge-icon-color.png
:align: center
:attr:`badge_icon_color` is an :class:`~kivy.properties.StringProperty`
and defaults to `None`.
"""
badge_bg_color = ColorProperty(None)
"""
Badge icon background color in (r, g, b, a) format.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
badge_icon: "plus"
badge_bg_color: "#b0f0d6"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-item-badge-bg-color.png
:align: center
:attr:`badge_bg_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
badge_font_size = NumericProperty(0)
"""
Badge icon font size.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
badge_icon: "plus"
badge_font_size: "24sp"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-item-badge-font-size.png
:align: center
:attr:`badge_font_size` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0`.
"""
active = BooleanProperty(False)
"""
Is the element active.
:attr:`active` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
_selected_region_width = NumericProperty("56dp")
_ripple_size = ListProperty([0, 0])
_release = BooleanProperty(False)
def on_active(
self, instance_navigation_rail_item, value_active: bool
) -> None:
"""Called when the value of `active` changes."""
self.animation_size_ripple_area(1 if value_active else 0)
def animation_size_ripple_area(self, value: int) -> None:
"""Animates the size/fade of the ripple area."""
Animation(
scale_value_x=value,
scale_value_y=value,
scale_value_z=value,
opacity=value,
d=0.25,
t=self.navigation_rail.ripple_transition,
).start(self.ids.ripple_widget)
def on_press(self) -> None:
"""Called when pressed on a panel element."""
self._release = False
self.active = True
self.navigation_rail.deselect_item(self)
self.navigation_rail.dispatch("on_item_press", self)
def on_release(self) -> None:
"""Called when released on a panel element."""
self._release = True
self.animation_size_ripple_area(0)
self.navigation_rail.dispatch("on_item_release", self)
class MDNavigationRail(MDCard, FakeRectangularElevationBehavior):
"""
:Events:
:attr:`on_item_press`
Called on the `on_press` event of menu item -
:class:`~MDNavigationRailItem`.
:attr:`on_item_release`
Called on the `on_release` event of menu item -
:class:`~MDNavigationRailItem`.
"""
radius = VariableListProperty(0, length=4)
"""
Rail radius.
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
and defaults to `[0, 0, 0, 0]`.
"""
padding = VariableListProperty([0, "36dp", 0, "36dp"], length=4)
"""
Padding between layout box and children:
[padding_left, padding_top, padding_right, padding_bottom].
:attr:`padding` is a :class:`~kivy.properties.VariableListProperty`
and defaults to `[0, '36dp', 0, '36dp']`.
"""
anchor = OptionProperty("top", options=["top", "bottom", "center"])
"""
The position of the panel with menu items.
Available options are: `'top'`, `'bottom'`, `'center'`.
.. rubric:: Top
.. code-block:: kv
MDNavigationRail:
anchor: "top"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-anchor-top.png
:align: center
.. rubric:: Center
.. code-block:: kv
MDNavigationRail:
anchor: "center"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-type-center.png
:align: center
.. rubric:: Bottom
.. code-block:: kv
MDNavigationRail:
anchor: "bottom"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-type-bottom.png
:align: center
:attr:`anchor` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'top'`.
"""
type = OptionProperty(
"labeled", options=["labeled", "selected", "unselected"]
)
"""
Type of switching menu items.
Available options are: `'labeled'`, `'selected'`, `'unselected'`.
.. rubric:: Labeled
.. code-block:: kv
MDNavigationRail:
type: "labeled"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-type-labeled.png
:align: center
.. rubric:: Selected
.. code-block:: kv
MDNavigationRail:
type: "selected"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-type-selected.gif
:align: center
.. rubric:: Unselected
.. code-block:: kv
MDNavigationRail:
type: "unselected"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-type-unselected.gif
:align: center
:attr:`type` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'labeled'`.
"""
text_color_item_normal = ColorProperty(None)
"""
The text color of the normal menu item (:class:`~MDNavigationRailItem`).
.. code-block:: kv
MDNavigationRail:
text_color_item_normal: app.theme_cls.primary_color
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-text-color-item-normal.png
:align: center
:attr:`text_color_item_normal` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
text_color_item_active = ColorProperty(None)
"""
The text color of the active menu item (:class:`~MDNavigationRailItem`).
.. code-block:: kv
MDNavigationRail:
text_color_item_active: app.theme_cls.primary_color
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-text-color-item-active.png
:align: center
:attr:`text_color_item_active` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
icon_color_item_normal = ColorProperty(None)
"""
The icon color of the normal menu item (:class:`~MDNavigationRailItem`).
.. code-block:: kv
MDNavigationRail:
icon_color_item_normal: app.theme_cls.primary_color
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-icon-color-item-normal.png
:align: center
:attr:`icon_color_item_normal` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
icon_color_item_active = ColorProperty(None)
"""
The icon color of the active menu item (:class:`~MDNavigationRailItem`).
.. code-block:: kv
MDNavigationRail:
icon_color_item_active: app.theme_cls.primary_color
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-icon-color-item-active.png
:align: center
:attr:`icon_color_item_active` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
selected_color_background = ColorProperty(None)
"""
Background color which will highlight the icon of the active menu item -
:class:`~MDNavigationRailItem` - in (r, g, b, a) format.
.. code-block:: kv
MDNavigationRail:
selected_color_background: "#e7e4c0"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-selected-color-background.png
:align: center
:attr:`selected_color_background` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
ripple_color_item = ColorProperty(None)
"""
Ripple effect color of menu items (:class:`~MDNavigationRailItem`)
in (r, g, b, a) format.
.. code-block:: kv
MDNavigationRail:
ripple_color_item: "#e7e4c0"
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-ripple-color-item.png
:align: center
:attr:`ripple_color_item` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
ripple_transition = StringProperty("out_cubic")
"""
Type of animation of the ripple effect when a menu item is selected.
:attr:`ripple_transition` is a :class:`~kivy.properties.StringProperty`
and defaults to `'ripple_transition'`.
"""
current_selected_item = NumericProperty(0)
"""
Index of the menu list item (:class:`~MDNavigationRailItem`) that will be
active by default
.. code-block:: kv
MDNavigationRail:
current_selected_item: 1
MDNavigationRailItem:
...
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-current-selected-item.png
:align: center
:attr:`current_selected_item` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0`.
"""
font_name = StringProperty("Roboto")
"""
Font path for menu item (:class:`~MDNavigationRailItem`) text.
.. code-block:: kv
MDNavigationRail:
MDNavigationRailItem:
text: "Python"
icon: "language-python"
font_name: "nasalization-rg.ttf"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-font-name.png
:align: center
:attr:`font_name` is an :class:`~kivy.properties.StringProperty`
and defaults to `'Roboto'`.
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_once(self.set_pos_menu_fab_buttons)
Clock.schedule_once(self.set_current_selected_item)
self.register_event_type("on_item_press")
self.register_event_type("on_item_release")
def on_item_press(self, *args) -> None:
"""
Called on the `on_press` event of menu item -
:class:`~MDNavigationRailItem`.
"""
def on_item_release(self, *args) -> None:
"""
Called on the `on_release` event of menu item -
:class:`~MDNavigationRailItem`.
"""
def deselect_item(
self, selected_navigation_rail_item: MDNavigationRailItem
) -> None:
"""
Sets the `active` value to `False` for all menu items
(:class:`~MDNavigationRailItem`) except the selected item.
Called when a menu item is touched.
"""
for navigation_rail_item in self.ids.box_items.children:
if selected_navigation_rail_item is not navigation_rail_item:
navigation_rail_item.active = False
def get_items(self) -> list:
"""Returns a list of :class:`~MDNavigationRailItem` objects"""
return self.ids.box_items.children
def set_pos_panel_items(
self,
instance_fab_button: Union[None, MDNavigationRailFabButton],
instance_menu_button: Union[None, MDNavigationRailFabButton],
) -> None:
"""Set :class:`~Paneltems` panel position with menu items."""
if self.anchor == "top":
if instance_fab_button:
self.ids.box_items.y = instance_fab_button.y - (
len(self.ids.box_items.children) * dp(56)
+ self.padding[1] * 2
+ dp(24)
)
else:
if not instance_menu_button:
self.ids.box_items.pos_hint = {"top": 1}
else:
self.ids.box_items.y = instance_menu_button.y - (
len(self.ids.box_items.children) * dp(56)
+ self.padding[1] * 2
)
elif self.anchor == "center":
self.ids.box_items.pos_hint = {"center_y": 0.5}
elif self.anchor == "bottom":
self.ids.box_items.y = dp(12)
def set_current_selected_item(self, interval: Union[int, float]) -> None:
"""Sets the active menu list item (:class:`~MDNavigationRailItem`)."""
if self.ids.box_items.children:
items = self.ids.box_items.children[:]
items.reverse()
if len(items) <= self.current_selected_item:
Logger.error(
f"MDNavigationRail:You have "
f"{len(self.ids.box_items.children)} menu items, but you "
f"set {self.current_selected_item} as the active item. "
f"The very first menu item will be set active."
)
index = 0
else:
index = self.current_selected_item
items[index].dispatch("on_press")
items[index].dispatch("on_release")
def set_pos_menu_fab_buttons(self, interval: Union[int, float]) -> None:
"""
Sets the position of the :class:`~MDNavigationRailFabButton` and
:class:`~MDNavigationRailMenuButton` buttons on the panel.
"""
fab_button = None # MDNavigationRailFabButton
menu_button = None # MDNavigationRailMenuButton
for widget in self.ids.box_buttons.children:
if isinstance(widget, MDNavigationRailFabButton):
fab_button = widget
if isinstance(widget, MDNavigationRailMenuButton):
menu_button = widget
if fab_button and menu_button:
def set_fab_button_y(interval):
fab_button.y = self.parent.height - (
menu_button.height
+ fab_button.height
+ self.padding[1]
+ dp(18)
)
self.set_pos_panel_items(fab_button, menu_button)
Clock.schedule_once(set_fab_button_y)
elif fab_button and not menu_button:
def set_fab_button_y(interval):
fab_button.y = self.parent.height - (
self.padding[1] + fab_button.height
)
self.set_pos_panel_items(fab_button, menu_button)
Clock.schedule_once(set_fab_button_y)
else:
Clock.schedule_once(
lambda x: self.set_pos_panel_items(fab_button, menu_button)
)
def add_widget(self, widget, *args, **kwargs):
if isinstance(widget, MDNavigationRailFabButton):
self.ids.box_buttons.add_widget(widget)
elif isinstance(widget, MDNavigationRailMenuButton):
self.ids.box_buttons.add_widget(widget)
elif isinstance(widget, MDNavigationRailItem):
widget.navigation_rail = self
self.ids.box_items.add_widget(widget)
elif isinstance(widget, (PanelRoot, PanelItems)):
return super().add_widget(widget)