"""
Components/SliverAppbar
=======================
.. versionadded:: 1.0.0
.. rubric:: MDSliverAppbar is a Material Design widget in KivyMD which gives
scrollable or collapsible
`MDTopAppBar `_
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-appbar-preview.gif
:align: center
.. note:: This widget is a modification of the
`silverappbar.py `_ module.
Usage
-----
.. code-block:: kv
MDScreen:
MDSliverAppbar:
MDTopAppBar:
[...]
MDSliverAppbarHeader:
# Custom content.
[...]
# Custom list.
MDSliverAppbarContent:
Anatomy
-------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-appbar-anatomy.png
:align: center
Example
-------
.. code-block:: python
from kivy.lang.builder import Builder
from kivymd.app import MDApp
from kivymd.uix.list import MDListItem
KV = '''
theme_bg_color: "Custom"
md_bg_color: "2d4a50"
MDListItemLeadingAvatar
source: "avatar.png"
MDListItemHeadlineText:
text: "Ibanez"
MDListItemSupportingText:
text: "GRG121DX-BKF"
MDListItemTertiaryText:
text: "$445,99"
MDListItemTrailingIcon:
icon: "guitar-electric"
MDScreen:
MDSliverAppbar:
background_color: "2d4a50"
hide_appbar: True
MDTopAppBar:
type: "medium"
MDTopAppBarLeadingButtonContainer:
MDActionTopAppBarButton:
icon: "arrow-left"
MDTopAppBarTitle:
text: "Sliver toolbar"
MDTopAppBarTrailingButtonContainer:
MDActionTopAppBarButton:
icon: "attachment"
MDActionTopAppBarButton:
icon: "calendar"
MDActionTopAppBarButton:
icon: "dots-vertical"
MDSliverAppbarHeader:
FitImage:
source: "bg.jpg"
MDSliverAppbarContent:
id: content
orientation: "vertical"
padding: "12dp"
theme_bg_color: "Custom"
md_bg_color: "2d4a50"
'''
class GuitarItem(MDListItem):
...
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return Builder.load_string(KV)
def on_start(self):
for x in range(10):
self.root.ids.content.add_widget(GuitarItem())
Example().run()
API break
=========
1.2.0 version
-------------
.. code-block:: kv
#:import SliverToolbar __main__.SliverToolbar
Root:
MDSliverAppbar:
[...]
MDSliverAppbarHeader:
[...]
MDSliverAppbarContent:
[...]
.. code-block:: python
class SliverToolbar(MDTopAppBar):
[...]
2.0.0 version
-------------
.. code-block:: kv
Root:
MDSliverAppbar:
[...]
MDTopAppBar:
[...]
MDSliverAppbarHeader:
[...]
MDSliverAppbarContent:
[...]
"""
__all__ = ("MDSliverAppbar", "MDSliverAppbarHeader", "MDSliverAppbarContent")
import os
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.lang.builder import Builder
from kivy.properties import (
BooleanProperty,
ColorProperty,
NumericProperty,
VariableListProperty,
ObjectProperty,
)
from kivy.uix.boxlayout import BoxLayout
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.appbar import MDTopAppBar
with open(
os.path.join(uix_path, "sliverappbar", "sliverappbar.kv"), encoding="utf-8"
) as kv_file:
Builder.load_string(kv_file.read())
class MDSliverAppbarException(Exception):
pass
class MDSliverAppbarContent(MDBoxLayout):
"""
Implements a box for a scrollable list of custom items.
For more information, see in the
:class:`~kivymd.uix.boxlayout.MDBoxLayout` class documentation.
"""
class MDSliverAppbarHeader(BoxLayout):
"""
Sliver app bar header class.
For more information, see in the
:class:`~kivy.uix.boxlayout.BoxLayout` class documentation.
"""
class MDSliverAppbar(ThemableBehavior, BoxLayout):
"""
Sliver appbar class.
For more information, see in the
:class:`~kivymd.theming.ThemableBehavior` and
:class:`~kivy.uix.boxlayout.BoxLayout`
classes documentation.
:Events:
:attr:`on_scroll_content`
Fired when the list of custom content is being scrolled.
"""
background_color = ColorProperty(None)
"""
Background color of appbar in (r, g, b, a) or string format.
:attr:`background_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
max_height = NumericProperty(Window.height / 2)
"""
Distance from top of screen to start of custom list content.
.. code-block:: kv
MDSliverAppbar:
max_height: "200dp"
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-appbar-max-height.png
:align: center
:attr:`max_height` is an :class:`~kivy.properties.NumericProperty`
and defaults to `Window.height / 2`.
"""
hide_appbar = BooleanProperty(None)
"""
Whether to hide the appbar when scrolling through a list
of custom content.
.. versionchanged:: 2.0.0
Rename `hide_toolbar` to `hide_appbar` attribute.
.. code-block:: kv
MDSliverAppbar:
hide_appbar: False
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-appbar-hide-appbar-false.gif
:align: center
.. code-block:: kv
MDSliverAppbar:
hide_appbar: True
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-appbar-hide-appbar-true.gif
:align: center
:attr:`hide_appbar` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `None`.
"""
radius = VariableListProperty([20], length=4)
"""
Box radius for custom item list.
.. code-block:: kv
MDSliverAppbar:
radius: 20
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-app-bar-radius.png
:align: center
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
and defaults to `[20]`.
"""
max_opacity = NumericProperty(1)
"""
Maximum background transparency value for the
:class:`~kivymd.uix.sliverappbar.sliverappbar.MDSliverAppbarHeader` class.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-app-bar-max-opacity.gif
:align: center
.. code-block:: kv
MDSliverAppbar:
max_opacity: .5
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sliver-app-bar-max-opacity-05.gif
:align: center
:attr:`max_opacity` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
_opacity = NumericProperty()
_scroll_was_moving = BooleanProperty(False)
_last_scroll_y_pos = 0.0
_appbar = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.register_event_type("on_scroll_content")
def on_hide_appbar(self, instance, value) -> None:
"""Fired when the `hide_appbar` value changes."""
if not value:
self.background_color = self.theme_cls.transparentColor
def on_scroll_content(
self,
instance: object = None,
value: float = 1.0,
direction: str = "up",
):
"""
Fired when the list of custom content is being scrolled.
:param instance: :class:`~MDSliverAppbar`
:param value: see :attr:`~kivy.uix.scrollview.ScrollView.scroll_y`
:param direction: scroll direction: 'up/down'
"""
def on_background_color(self, instance, color) -> None:
"""Fired when the `background_color` value changes."""
if self._appbar:
self._appbar.canvas.get_group("md-top-app-bar-color")[
0
].rgba = color
def on_vbar(self) -> None:
if not self.background_color:
self.background_color = self.theme_cls.primaryColor
scroll_box = self.ids.scroll_box
vbar = self.ids.scroll.vbar
appbar_percent = (self._appbar.height / scroll_box.height) * 100
current_percent = (vbar[0] + vbar[1]) * 100
percent_min = (
1 - self.max_height / scroll_box.height
) * 100 + appbar_percent
if self._scroll_was_moving:
direction = self._get_direction_swipe(self.ids.scroll.scroll_y)
self._last_scroll_y_pos = self.ids.scroll.scroll_y
self.dispatch(
"on_scroll_content", self.ids.scroll.scroll_y, direction
)
if self.hide_appbar:
if percent_min <= current_percent:
opacity = (current_percent - percent_min) / (100 - percent_min)
self._opacity = self.max_opacity * (1 - opacity)
self.background_color = self.background_color[0:3] + [
1 - opacity
]
else:
self.background_color = self.background_color[0:3] + [1]
def add_widget(self, widget, index=0, canvas=None):
if isinstance(widget, MDSliverAppbarContent):
Clock.schedule_once(lambda x: self._set_radius(widget))
self.ids.scroll_box.add_widget(widget)
elif isinstance(widget, MDSliverAppbarHeader):
self.ids.header.add_widget(widget)
elif isinstance(widget, MDTopAppBar):
self._appbar = widget
widget.pos_hint = {"top": 1}
self.ids.float_box.add_widget(widget)
else:
super().add_widget(widget, index=index, canvas=canvas)
def on__appbar(self, instance, value):
def set_rgba_appbar(*args):
if self.hide_appbar:
value.theme_elevation_level = "Custom"
value.elevation_level = 0
value.theme_shadow_color = "Custom"
value.shadow_color = self.theme_cls.transparentColor
value.md_bg_color = self.theme_cls.transparentColor
value.canvas.get_group("md-top-app-bar-color")[
0
].rgba = self.theme_cls.transparentColor
Clock.schedule_once(set_rgba_appbar, 0.5)
def _set_radius(self, instance: MDSliverAppbarContent):
instance.radius = self.radius
def _get_direction_swipe(self, current_percent: float):
if self._last_scroll_y_pos > current_percent:
direction = "up"
else:
direction = "down"
return direction