kivymd / KivyMD

KivyMD is a collection of Material Design compliant widgets for use with Kivy, a framework for cross-platform, touch-enabled graphical applications. https://youtube.com/c/KivyMD https://twitter.com/KivyMD https://habr.com/ru/users/kivymd https://stackoverflow.com/tags/kivymd
https://kivymd.readthedocs.io
MIT License
2.26k stars 674 forks source link

MDNavigationRail Active #1719

Open RicardoDazzling opened 4 months ago

RicardoDazzling commented 4 months ago

Description of the Bug

When the active property from a MDNavigationRailItem is set from the code, the navigation rail don't set this to active and the hover not works anymore.

Code and Logs

I picked up this code from the navigation rail documentation, only change the active from the first Item to True.

from kivy.lang import Builder
from kivy.properties import StringProperty

from kivymd.app import MDApp
from kivymd.uix.navigationrail import MDNavigationRailItem

KV = '''
<CommonNavigationRailItem>

    MDNavigationRailItemIcon:
        icon: root.icon

    MDNavigationRailItemLabel:
        text: root.text

MDBoxLayout:

    MDNavigationRail:
        type: "selected"

        MDNavigationRailMenuButton:
            icon: "menu"

        MDNavigationRailFabButton:
            icon: "home"

        CommonNavigationRailItem:
            id: first
            active: True
            icon: "folder-outline"
            text: "Files"

        CommonNavigationRailItem:
            icon: "bookmark-outline"
            text: "Bookmark"

        CommonNavigationRailItem:
            icon: "library-outline"
            text: "Library"

    MDScreen:
        md_bg_color: self.theme_cls.secondaryContainerColor
'''

class CommonNavigationRailItem(MDNavigationRailItem):
    text = StringProperty()
    icon = StringProperty()

class Example(MDApp):
    def build(self):
        build = Builder.load_string(KV)
        # Or when do this too:
        # build.ids.first.active = True
        return build

Example().run()

Screenshots

hover
Hover only works in the non active item

Versions

j4ggr commented 2 months ago

I had a similar problem and solved it with a "default_active" property. Based on your code, it would look like this:

First, create your own NavigationRail that inherits from MDNavigationRail. Add a StringProperty with the name "default_active" or something like that. Then define a corresponding callback function in which the desired default item is automatically activated by calling the "trigger_action" method. In my following example, I use the "text" attribute for comparison. But you could also do it using another attribute that occurs in all items.

Important! Call the callback function after initializing the parent class.

Finally, change the "MDNavigationRail" name in the KV string with the name of the class you created (here simply "NavigationRail") and add your desired "default_active" below it.

Here is the adapted code:

from kivy.lang import Builder
from kivy.properties import StringProperty

from kivy.clock import Clock
from kivymd.app import MDApp
from kivymd.uix.navigationrail import MDNavigationRail
from kivymd.uix.navigationrail import MDNavigationRailItem

KV = '''
<CommonNavigationRailItem>

    MDNavigationRailItemIcon:
        icon: root.icon

    MDNavigationRailItemLabel:
        text: root.text

MDBoxLayout:

    NavigationRail:
        type: "selected"
        default_active: "Files"

        MDNavigationRailMenuButton:
            icon: "menu"

        MDNavigationRailFabButton:
            icon: "home"

        CommonNavigationRailItem:
            id: first
            icon: "folder-outline"
            text: "Files"

        CommonNavigationRailItem:
            icon: "bookmark-outline"
            text: "Bookmark"

        CommonNavigationRailItem:
            icon: "library-outline"
            text: "Library"

    MDScreen:
        md_bg_color: self.theme_cls.secondaryContainerColor
'''

class NavigationRail(MDNavigationRail):

    default_active: str = StringProperty("")

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        Clock.schedule_once(
            lambda x: self.on_default_active(self, self.default_active))

    def on_default_active(self, caller, default_active: str) -> None:
        for item in self.get_items():
            if item.text == default_active:
                item.trigger_action()
                return

class CommonNavigationRailItem(MDNavigationRailItem):
    text = StringProperty()
    icon = StringProperty()

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

Example().run()