Closed kodi-rc closed 2 years ago
Same problem here using Python 3.9.5 and KivyMD 0.104.1. Is there any possibility of making the text smaller as in the Kitchen Sink demo? Any option to add to the widget maybe? I'm just starting with Kivy though.
@rodrigofcaba Yes, the MDNavigationRail widget needs to be improved, we know about that...
@HeaTTheatR It was just a question, because I couldn't find properties or any information in the docs. I assume that at this moment there is no way to change the text size then. I'll take a look to the source code anyways. Thanks in advance!
@rodrigofcaba You can suggest your changes as pull request :)
Sure, If I manage to improve something I will!
here is my suggestion for this issue...
added property ->
normal_width = NumericProperty(dp(72))
# width used for close state
# also used for railitems icon width
open_width = NumericProperty(dp(72)*4)
# width used for open state
# also used to set railitems width
result -> exmpl 1.
exmpl 2.
"""
Components/Navigation Rail
==========================
.. seealso::
`Material Design spec, Navigation rail <https://material.io/components/navigation-rail>`_
.. rubric:: Navigation rails provide ergonomic movement between primary destinations in apps.
.. 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.factory import Factory
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
#:import get_color_from_hex kivy.utils.get_color_from_hex
<MyTile@SmartTileWithStar>
size_hint_y: None
height: "240dp"
MDBoxLayout:
orientation: "vertical"
MDToolbar:
title: "MDNavigationRail"
md_bg_color: rail.md_bg_color
MDBoxLayout:
MDNavigationRail:
id: rail
md_bg_color: get_color_from_hex("#344954")
color_normal: get_color_from_hex("#718089")
color_active: get_color_from_hex("#f3ab44")
MDNavigationRailItem:
icon: "language-cpp"
text: "C++"
MDNavigationRailItem:
icon: "language-python"
text: "Python"
MDNavigationRailItem:
icon: "language-swift"
text: "Swift"
MDBoxLayout:
padding: "24dp"
ScrollView:
MDList:
id: box
cols: 3
spacing: "10dp"
'''
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
for i in range(9):
tile = Factory.MyTile(source="cpp.png")
tile.stars = 5
self.root.ids.box.add_widget(tile)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-usage.png
:align: center
"""
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import (
BooleanProperty,
ColorProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
)
from kivy.uix.behaviors import ButtonBehavior
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import CircularRippleBehavior, HoverBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFloatingActionButton
from kivymd.uix.card import MDCard
from kivymd.uix.label import MDLabel
Builder.load_string(
"""
#:import StiffScrollEffect kivymd.effects.stiffscroll.StiffScrollEffect
<NavigationRailTitle>
id: title_box
padding: "12dp", "8dp", "8dp", "8dp"
spacing: "16dp"
MDIconButton:
id: title_icon
icon: root.icon
pos_hint: {"center_x": .5, "center_y": .5}
on_release:
root.navigation_rail.rail_state = "open" \
if root.navigation_rail.rail_state == "close" else "close"
MDLabel:
id: lbl_title
text: root.title
size_hint: None, None
-text_size: dp(140), None
size: dp(140), self.texture_size[1]
pos_hint: {"center_y": .5}
opacity: 0
markup: True
MDIconButton:
id: icon_settings
opacity: 0
opposite_colors: True
icon: "cog"
pos_hint: {"center_y": .5}
<BaseNavigationRailFloatingButton>
theme_text_color: "Custom"
canvas.before:
Color:
rgba: root.md_bg_color
RoundedRectangle:
pos: dp(8), self.y - self._padding_right / 2 + dp(1.5)
size:
self.width + self._canvas_width - dp(3), \
self.height + self._padding_right - dp(3)
radius: [self.height / 2]
Color:
rgba: 1, 1, 1, self._alpha
Rectangle:
texture: self._lbl_text.texture
size: self._lbl_text.texture_size
pos: self.width + dp(8), self.y + dp(18)
<BaseNavigationRailBoxItem>
spacing: "8dp"
padding: "8dp",
<MDNavigationRailItem>
orientation: "vertical"
spacing: "12dp" if self.orientation == "horizontal" else "0dp"
adaptive_height: True
# on_size: print(self.size)
MDIcon:
id: btn_icon
# md_bg_color: [0,0.5,0,0.5]
size_hint: None,None
size: [root.normal_width-root.padding[0]-root.padding[2],self.font_size]
icon: root.icon
font_size: "24dp"
halign: "center"
theme_text_color: "Custom"
text_color:
root.theme_cls.text_color \
if not root._color_normal else root._color_normal
pos_hint: {"center_x": .5,"center_y": .5}
MDLabel:
id: lbl_text
# md_bg_color: [0,0,0,0.5]
text: root.text
size_hint_y: None
height: self.texture_size[1]
halign: "left" if root.orientation == "horizontal" else "center"
markup: True
theme_text_color: "Custom"
text_color:
btn_icon.text_color \
if root.navigation_rail.visible == "Persistent" else (0, 0, 0, 0)
pos_hint: {"center_x": .5,"center_y": .5}
<MDNavigationRail>
orientation: "vertical"
padding: 0, "8dp", 0, "8dp"
elevation: root.elevation
MDBoxLayout:
id: box_title
size_hint: None, None
size: root.width * 2, "56dp"
ScrollView:
effect_cls: StiffScrollEffect
bar_width: 0
MDList:
id: box
spacing: "10dp"
"""
)
__all__ = ("MDNavigationRail",)
class BaseNavigationRailBoxItem(MDBoxLayout):
"""The base class to which the menu items will be added
(:class:`~MDNavigationRailItem` classes)."""
class BaseNavigationRailFloatingButton(MDFloatingActionButton):
"""Class for a custom MDFloatingActionButton that can stretch."""
text = StringProperty()
"""
The text will be placed to the left of the button icon..
:attr:`text` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
_canvas_width = NumericProperty(0)
_alpha = NumericProperty(0)
_padding_right = NumericProperty(0)
def __init__(self, **kwargs):
self._lbl_text = MDLabel(markup=True)
super().__init__(**kwargs)
def on_text(self, instance, value):
self._lbl_text.text = value
class NavigationRailTitle(MDBoxLayout):
navigation_rail = ObjectProperty()
"""
`:class:`~kivymd.uix.navigationrail.MDNavigationRail` object.
:attr:`navigation_rail` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
icon = StringProperty("menu")
"""
Icon item.
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `'blank'`.
"""
title = StringProperty("Rail")
"""
Text title.
:attr:`title` is an :class:`~kivy.properties.StringProperty`
and defaults to `'Rail'`.
"""
class MDNavigationRailItem(
ThemableBehavior,
HoverBehavior,
CircularRippleBehavior,
ButtonBehavior,
BaseNavigationRailBoxItem,
):
icon = StringProperty("checkbox-blank")
"""
Icon item.
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `'checkbox-blank'`.
"""
text = StringProperty()
"""
Text item.
:attr:`text` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
navigation_rail = ObjectProperty()
"""
`:class:`~kivymd.uix.navigationrail.MDNavigationRail` object.
:attr:`navigation_rail` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
color_normal = ColorProperty(None)
"""See :attr:`~MDNavigationRail.color_normal` attribute."""
color_active = ColorProperty(None)
"""See :attr:`~MDNavigationRail.color_active` attribute."""
_color_normal = ColorProperty(None)
_color_active = ColorProperty(None)
normal_width = NumericProperty(dp(72))
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._no_ripple_effect = True
# Clock.schedule_once(self.set_width)
def set_width(self, interval):
"""Sets the size of the menu item."""
self.size_hint = (None, None)
self.width = self.navigation_rail.normal_width # (self.navigation_rail.normal_width, self.navigation_rail.width)
def on_enter(self):
if self.navigation_rail.use_hover_behavior:
Animation(
md_bg_color=self.theme_cls.bg_darkest
if not self.navigation_rail.hover_bg
else self.navigation_rail.hover_bg,
t=self.navigation_rail.color_transition,
d=self.navigation_rail.color_change_duration,
).start(self)
def on_leave(self):
if self.navigation_rail.use_hover_behavior:
Animation(
md_bg_color=self.theme_cls.bg_light
if self.navigation_rail.md_bg_color == self.theme_cls.bg_light
else self.navigation_rail.md_bg_color,
t=self.navigation_rail.color_transition,
d=self.navigation_rail.color_change_duration,
).start(self)
def on_release(self):
self.navigation_rail.set_color_menu_item(self)
self.navigation_rail.dispatch("on_item_switch", self)
def on_visible(self, instance, value):
if value in ("Selected", "Unlabeled"):
Clock.schedule_once(self.set_item_label_transparency)
def set_item_label_transparency(self, interval):
self.ids.lbl_text.text_color = (0, 0, 0, 0)
class MDNavigationRail(MDCard):
"""
:Events:
`on_item_switch`
Called when the menu item is switched.
`on_open`
Called when a rail is opened.
`on_close`
Called when a rail is closed.
`on_action_button`
"""
use_hover_behavior = BooleanProperty(False)
"""
Whether to use the HoverBehavior effect for menu items.
.. code-block:: kv
MDNavigationRail:
use_hover_behavior: True
hover_bg: 0, 0, 0, .2
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-hover-behavior.gif
:align: center
:attr:`use_hover_behavior` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
hover_bg = ColorProperty(None)
"""
The background color for the menu item.
Used when :attr:`use_hover_behavior` parameter is `True`.
"""
use_resizeable = BooleanProperty(False)
"""
Allows you to change the width of the rail (open/close).
.. code-block:: python
from kivy.factory import Factory
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
#:import get_color_from_hex kivy.utils.get_color_from_hex
<MyTile@SmartTileWithStar>
size_hint_y: None
height: "240dp"
MDBoxLayout:
orientation: "vertical"
MDToolbar:
title: "MDNavigationRail"
md_bg_color: rail.md_bg_color
left_action_items: [["menu", lambda x: app.rail_open()]]
MDBoxLayout:
MDNavigationRail:
id: rail
md_bg_color: get_color_from_hex("#344954")
color_normal: get_color_from_hex("#718089")
color_active: get_color_from_hex("#f3ab44")
use_resizeable: True
MDNavigationRailItem:
icon: "language-cpp"
text: "C++"
MDNavigationRailItem:
icon: "language-java"
text: "Java"
MDNavigationRailItem:
icon: "language-swift"
text: "Swift"
MDBoxLayout:
padding: "24dp"
ScrollView:
MDList:
id: box
cols: 3
spacing: "12dp"
'''
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def rail_open(self):
if self.root.ids.rail.rail_state == "open":
self.root.ids.rail.rail_state = "close"
else:
self.root.ids.rail.rail_state = "open"
def on_start(self):
for i in range(9):
tile = Factory.MyTile(source="kitten.png")
tile.stars = 5
self.root.ids.box.add_widget(tile)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-use-resizeable.gif
:align: center
:attr:`use_resizeable` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
use_title = BooleanProperty(False)
"""
Whether to use an additional panel at the top of the rail.
.. code-block:: kv
MDNavigationRail:
use_resizeable: True
use_title: True
icon_title: "logo.png"
text_title: "[b][color=#ffffff]Example[/color][/b]"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-use-title.gif
:align: center
:attr:`use_title` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
icon_title = StringProperty("menu")
"""
Icon (name or path to `png` file) for :class:`~NavigationRailTitle` class.
:attr:`icon_title` is an :class:`~kivy.properties.StringProperty`
and defaults to `'menu'`.
"""
text_title = StringProperty("Rail")
"""
Text title for :class:`~NavigationRailTitle` class.
:attr:`text_title` is an :class:`~kivy.properties.StringProperty`
and defaults to `'Rail'`.
"""
use_action_button = BooleanProperty(False)
"""
Should :class:`~MDFloatingActionButton` button be used.
.. code-block:: kv
MDNavigationRail:
use_action_button: True
action_text_button: "COMPOSE"
on_action_button: print(args)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-use-action-button.gif
:align: center
:attr:`use_action_button` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
action_icon_button = StringProperty("plus")
"""
Icon of :attr:`~use_action_button`.
:attr:`action_icon_button` is an :class:`~kivy.properties.StringProperty`
and defaults to `'plus'`.
"""
action_text_button = StringProperty()
"""
Text of :attr:`~use_action_button`.
:attr:`action_text_button` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
action_color_button = ColorProperty(None)
"""
Text of :attr:`~use_action_button`.
:attr:`action_color_button` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
color_normal = ColorProperty(None)
"""
Color normal of item menu.
:attr:`color_normal` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
color_active = ColorProperty(None)
"""
Color active of item menu.
:attr:`color_active` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
visible = OptionProperty(
"Persistent", options=["Selected", "Persistent", "Unlabeled"]
)
"""
Item label visible type.
Available options are: `'Selected'`, `'Persistent'`, `'Unlabeled'`.
Persistent
~~~~~~~~~~
.. code-block:: kv
MDNavigationRail:
visible: "Persistent"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-visible-persistent.gif
:align: center
Selected
~~~~~~~~
.. code-block:: kv
MDNavigationRail:
visible: "Selected"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-visible-selected.gif
:align: center
Unlabeled
~~~~~~~~~
.. code-block:: kv
MDNavigationRail:
visible: "Unlabeled"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-rail-visible-unlabeled.gif
:align: center
:attr:`visible` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'Persistent'`.
"""
color_transition = StringProperty("linear")
"""
Animation type when changing the color of a menu item.
:attr:`color_transition` is a :class:`~kivy.properties.StringProperty`
and defaults to `'linear'`.
"""
color_change_duration = NumericProperty(0.2)
"""
Animation duration when changing menu item color.
:attr:`color_change_duration` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
rail_state = OptionProperty("close", options=("close", "open"))
"""
Closed or open rails.
:attr:`rail_state` is a :class:`~kivy.properties.OptionProperty`
and defaults to `'close'`.
"""
normal_width = NumericProperty(dp(72))
open_width = NumericProperty(dp(72)*4)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.floating_action_button = None
self.elevation = 0
self.register_event_type("on_action_button")
self.register_event_type("on_item_switch")
self.register_event_type("on_open")
self.register_event_type("on_close")
Clock.schedule_once(self.set_items_visible)
Clock.schedule_once(self.set_width)
Clock.schedule_once(self.set_action_icon_button)
Clock.schedule_once(self.set_action_text_button)
Clock.schedule_once(self.set_box_title_size)
Clock.schedule_once(self.set_action_color_button)
Clock.schedule_once(self.set_items_color)
def anim_color_normal(self, item):
color = (
self.theme_cls.text_color
if not item.color_normal
else item.color_normal
)
Animation(
_color_normal=color,
t=self.color_transition,
d=self.color_change_duration,
).start(item)
if item.visible == "Selected":
Animation(
text_color=(0, 0, 0, 0),
t=self.color_transition,
d=self.color_change_duration,
).start(item.ids.lbl_text)
def anim_color_active(self, item):
color = (
self.theme_cls.primary_color
if not item.color_active
else item.color_active
)
Animation(
_color_normal=color,
t=self.color_transition,
d=self.color_change_duration,
).start(item)
if item.visible == "Selected":
item.ids.lbl_text.text_color = item._color_normal
Animation(
text_color=color,
t=self.color_transition,
d=self.color_change_duration,
).start(item.ids.lbl_text)
def item_switch(self, instance_item):
instance_item.dispatch("on_release")
def add_widget(self, widget, index=0, canvas=None):
if isinstance(widget, MDNavigationRailItem):
widget.navigation_rail = self
self.bind(color_normal=widget.setter("color_normal"))
self.bind(color_normal=widget.setter("_color_normal"))
self.bind(color_active=widget.setter("color_active"))
self.ids.box.add_widget(widget)
else:
return super().add_widget(widget)
def open(self):
def set_opacity_title_component(*args):
if self.use_title:
Animation(opacity=1, d=0.2).start(
self.ids.box_title.children[0].ids.lbl_title
)
Animation(opacity=1, d=0.2).start(
self.ids.box_title.children[0].ids.icon_settings
)
if self.use_resizeable:
anim = Animation(width=self.open_width, d=0.2)
anim.bind(on_complete=set_opacity_title_component)
anim.start(self)
if self.floating_action_button:
Animation(
_canvas_width=self.floating_action_button.width + dp(124),
_padding_right=dp(8),
_alpha=1,
d=0.2,
).start(self.floating_action_button)
self.dispatch("on_open")
def close(self):
if self.use_resizeable:
Animation(width=self.normal_width, d=0.2).start(self)
if self.use_title:
Animation(opacity=0, d=0.2).start(
self.ids.box_title.children[0].ids.lbl_title
)
Animation(opacity=0, d=0.02).start(
self.ids.box_title.children[0].ids.icon_settings
)
if self.floating_action_button:
Animation(
_canvas_width=0,
_padding_right=0,
d=0.2,
_alpha=0,
).start(self.floating_action_button)
self.dispatch("on_close")
def on_rail_state(self, instance, value):
if value == "open":
self.open()
elif value == "close":
self.close()
def on_item_switch(self, instance_item):
"""Called when the menu item is switched."""
def on_open(self):
"""Called when a rail is opened."""
def on_close(self):
"""Called when a rail is closed."""
def on_action_button(self, floating_action_button):
"""Called when the `MDFloatingActionButton` is pressed."""
def on_visible(self, instance, value):
for widget in self.ids.box.children:
if isinstance(widget, MDNavigationRailItem):
widget.visible = value
def on_use_title(self, instance, value):
def add_title(interval):
if value:
self.ids.box_title.add_widget(
NavigationRailTitle(
navigation_rail=self,
icon=self.icon_title,
title=self.text_title,
)
)
Clock.schedule_once(add_title)
def on_use_resizeable(self, instance, value):
def add_item(interval):
if value:
for widget in self.ids.box.children:
if isinstance(widget, MDNavigationRailItem):
widget.orientation = "horizontal"
self.ids.box.spacing = "20dp"
self.use_hover_behavior = False
widget._no_ripple_effect = True
widget.size_hint_x = None
widget.width = self.open_width
widget.normal_width = self.normal_width
Clock.schedule_once(add_item)
def on_use_action_button(self, instance, value):
if value:
rail_box = BaseNavigationRailBoxItem(
size_hint=(None, None),
width=self.width,
padding=dp(8),
)
self.floating_action_button = BaseNavigationRailFloatingButton(
pos_hint={"top": 1},
)
self.floating_action_button.bind(
on_release=self.press_floating_action_button
)
rail_box.height = self.floating_action_button.height + dp(16)
rail_box.add_widget(self.floating_action_button)
self.ids.box.add_widget(rail_box)
self.ids.box.children.reverse()
def press_floating_action_button(self, floating_action_button):
self.dispatch("on_action_button", floating_action_button)
def set_action_color_button(self, interval):
if self.floating_action_button and self.action_color_button:
self.floating_action_button.md_bg_color = self.action_color_button
def set_width(self, interval):
self.size_hint_x = None
self.width = self.normal_width
def set_box_title_size(self, interval):
if not self.use_title:
self.remove_widget(self.ids.box_title)
def set_action_icon_button(self, interval):
if self.floating_action_button:
self.floating_action_button.icon = self.action_icon_button
def set_action_text_button(self, interval):
if self.floating_action_button:
self.floating_action_button.text = self.action_text_button
def set_color_menu_item(self, instance_item):
for item in self.ids.box.children:
if isinstance(item, MDNavigationRailItem):
if instance_item is item:
self.anim_color_active(item)
else:
self.anim_color_normal(item)
def set_items_color(self, interval):
if not self.color_normal:
self.color_normal = self.theme_cls.text_color
if not self.color_active:
self.color_active = self.theme_cls.bg_light
def set_items_visible(self, interval):
# If the user does not set the `visible` parameter.
if self.visible == "Persistent":
for widget in self.ids.box.children:
if isinstance(widget, MDNavigationRailItem):
widget.visible = "Persistent"
Description of the Bug
MDNavigationRailItem text is not displayed on a single line, with slightly longer text, when the rail is opened. When the MDNavigationRail is closed and the item text is a little longer, the height of the MDNavigationRailitem is very large
Code and Logs
Screenshots
Versions