613 lines
17 KiB
Python
613 lines
17 KiB
Python
|
"""
|
||
|
Components/Toolbar
|
||
|
==================
|
||
|
|
||
|
.. seealso::
|
||
|
|
||
|
`Material Design spec, App bars: top <https://material.io/components/app-bars-top>`_
|
||
|
|
||
|
`Material Design spec, App bars: bottom <https://material.io/components/app-bars-bottom/app-bars-bottom.html>`_
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/app-bar-top.png
|
||
|
:align: center
|
||
|
|
||
|
`KivyMD` provides the following toolbar positions for use:
|
||
|
|
||
|
- Top_
|
||
|
- Bottom_
|
||
|
|
||
|
.. Top:
|
||
|
Top
|
||
|
---
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
from kivy.lang import Builder
|
||
|
|
||
|
from kivymd.app import MDApp
|
||
|
|
||
|
KV = '''
|
||
|
BoxLayout:
|
||
|
orientation: "vertical"
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "MDToolbar"
|
||
|
|
||
|
MDLabel:
|
||
|
text: "Content"
|
||
|
halign: "center"
|
||
|
'''
|
||
|
|
||
|
|
||
|
class Test(MDApp):
|
||
|
def build(self):
|
||
|
return Builder.load_string(KV)
|
||
|
|
||
|
|
||
|
Test().run()
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-1.png
|
||
|
:align: center
|
||
|
|
||
|
Add left menu
|
||
|
-------------
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "MDToolbar"
|
||
|
left_action_items: [["menu", lambda x: app.callback()]]
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-2.png
|
||
|
:align: center
|
||
|
|
||
|
Add right menu
|
||
|
--------------
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "MDToolbar"
|
||
|
right_action_items: [["dots-vertical", lambda x: app.callback()]]
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-3.png
|
||
|
:align: center
|
||
|
|
||
|
Add two item to the right menu
|
||
|
------------------------------
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "MDToolbar"
|
||
|
right_action_items: [["dots-vertical", lambda x: app.callback_1()], ["clock", lambda x: app.callback_2()]]
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-4.png
|
||
|
:align: center
|
||
|
|
||
|
Change toolbar color
|
||
|
--------------------
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "MDToolbar"
|
||
|
md_bg_color: app.theme_cls.accent_color
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-5.png
|
||
|
:align: center
|
||
|
|
||
|
Change toolbar text color
|
||
|
-------------------------
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "MDToolbar"
|
||
|
specific_text_color: app.theme_cls.accent_color
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-6.png
|
||
|
:align: center
|
||
|
|
||
|
Shadow elevation control
|
||
|
------------------------
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "Elevation 10"
|
||
|
elevation: 10
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-7.png
|
||
|
:align: center
|
||
|
|
||
|
.. Bottom:
|
||
|
Bottom
|
||
|
------
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/app-bar-bottom.png
|
||
|
:align: center
|
||
|
|
||
|
Usage
|
||
|
-----
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
from kivy.lang import Builder
|
||
|
|
||
|
from kivymd.app import MDApp
|
||
|
|
||
|
KV = '''
|
||
|
BoxLayout:
|
||
|
|
||
|
# Will always be at the bottom of the screen.
|
||
|
MDBottomAppBar:
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "Title"
|
||
|
icon: "git"
|
||
|
type: "bottom"
|
||
|
left_action_items: [["menu", lambda x: x]]
|
||
|
'''
|
||
|
|
||
|
|
||
|
class Test(MDApp):
|
||
|
def build(self):
|
||
|
return Builder.load_string(KV)
|
||
|
|
||
|
|
||
|
Test().run()
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-8.png
|
||
|
:align: center
|
||
|
|
||
|
Event on floating button
|
||
|
------------------------
|
||
|
|
||
|
Event ``on_action_button``:
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDBottomAppBar:
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "Title"
|
||
|
icon: "git"
|
||
|
type: "bottom"
|
||
|
left_action_items: [["menu", lambda x: x]]
|
||
|
on_action_button: app.callback(self.icon)
|
||
|
|
||
|
Floating button position
|
||
|
------------------------
|
||
|
|
||
|
Mode:
|
||
|
|
||
|
- `'free-end'`
|
||
|
- `'free-center'`
|
||
|
- `'end'`
|
||
|
- `'center'`
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDBottomAppBar:
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "Title"
|
||
|
icon: "git"
|
||
|
type: "bottom"
|
||
|
left_action_items: [["menu", lambda x: x]]
|
||
|
mode: "end"
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-9.png
|
||
|
:align: center
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDBottomAppBar:
|
||
|
|
||
|
MDToolbar:
|
||
|
title: "Title"
|
||
|
icon: "git"
|
||
|
type: "bottom"
|
||
|
left_action_items: [["menu", lambda x: x]]
|
||
|
mode: "free-end"
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toolbar-10.png
|
||
|
:align: center
|
||
|
|
||
|
.. seealso::
|
||
|
|
||
|
`Components-Bottom-App-Bar <https://github.com/kivymd/KivyMD/wiki/Components-Bottom-App-Bar>`_
|
||
|
"""
|
||
|
|
||
|
from kivy.animation import Animation
|
||
|
from kivy.clock import Clock
|
||
|
from kivy.core.window import Window
|
||
|
from kivy.lang import Builder
|
||
|
from kivy.metrics import dp
|
||
|
from kivy.properties import (
|
||
|
ListProperty,
|
||
|
NumericProperty,
|
||
|
OptionProperty,
|
||
|
StringProperty,
|
||
|
)
|
||
|
from kivy.uix.boxlayout import BoxLayout
|
||
|
from kivy.uix.floatlayout import FloatLayout
|
||
|
|
||
|
from kivymd.theming import ThemableBehavior
|
||
|
from kivymd.uix.behaviors import (
|
||
|
RectangularElevationBehavior,
|
||
|
SpecificBackgroundColorBehavior,
|
||
|
)
|
||
|
from kivymd.uix.button import MDFloatingActionButton, MDIconButton
|
||
|
|
||
|
Builder.load_string(
|
||
|
"""
|
||
|
#:import m_res kivymd.material_resources
|
||
|
|
||
|
|
||
|
<MDActionBottomAppBarButton>:
|
||
|
md_bg_color: self.theme_cls.primary_color
|
||
|
|
||
|
canvas.before:
|
||
|
PushMatrix
|
||
|
Scale:
|
||
|
origin: self.center
|
||
|
x: root._scale_x
|
||
|
y: root._scale_y
|
||
|
canvas.after:
|
||
|
PopMatrix
|
||
|
|
||
|
|
||
|
<MDToolbar>
|
||
|
size_hint_y: None
|
||
|
height: root.theme_cls.standard_increment
|
||
|
padding: [root.theme_cls.horizontal_margins - dp(12), 0]
|
||
|
opposite_colors: True
|
||
|
elevation: root.elevation
|
||
|
md_bg_color: self.theme_cls.primary_color if root.type != "bottom" else [0, 0, 0, 0]
|
||
|
|
||
|
canvas:
|
||
|
Color:
|
||
|
rgba: root.theme_cls.primary_color
|
||
|
RoundedRectangle:
|
||
|
pos:
|
||
|
self.pos \
|
||
|
if root.mode == "center" else \
|
||
|
(self.width - root.action_button.width + dp(6), self.y)
|
||
|
size:
|
||
|
(((self.width - root.action_button.width) / 2 - dp(6), self.height) \
|
||
|
if root.mode == "center" else \
|
||
|
(root.action_button.width - dp(6), self.height)) if root.type == "bottom" else (0, 0)
|
||
|
radius:
|
||
|
(0, root.round, 0, 0) if root.mode == "center" else (root.round, 0, 0, 0)
|
||
|
Rectangle:
|
||
|
pos:
|
||
|
((self.width / 2 - root.action_button.width / 2) - dp(6), self.y - root._shift) \
|
||
|
if root.mode == "center" else \
|
||
|
(self.width - root.action_button.width * 2 - dp(6), self.y - root._shift)
|
||
|
size:
|
||
|
(root.action_button.width + dp(6) * 2, self.height - root._shift * 2) \
|
||
|
if root.type == "bottom" else (0, 0)
|
||
|
RoundedRectangle:
|
||
|
pos:
|
||
|
((self.width + root.action_button.width) / 2 + dp(6), self.y) \
|
||
|
if root.mode == "center" else self.pos
|
||
|
size:
|
||
|
(((self.width - root.action_button.width) / 2 + dp(6), self.height) \
|
||
|
if root.mode == "center" else \
|
||
|
((self.width - root.action_button.width * 2 - dp(6)), self.height)) \
|
||
|
if root.type == "bottom" else (0, 0)
|
||
|
radius: (root.round, 0, 0, 0) if root.mode == "center" else (0, root.round, 0, 0)
|
||
|
Color:
|
||
|
rgba: 1, 1, 1, 1
|
||
|
Ellipse:
|
||
|
pos:
|
||
|
(self.center[0] - root.action_button.width / 2 - dp(6), self.center[1] - root._shift * 2) \
|
||
|
if root.mode == "center" else \
|
||
|
(self.width - root.action_button.width * 2 - dp(6), self.center[1] - root._shift * 2)
|
||
|
size:
|
||
|
(root.action_button.width + dp(6) * 2, root.action_button.width) \
|
||
|
if root.type == "bottom" else (0, 0)
|
||
|
angle_start: root._angle_start
|
||
|
angle_end: root._angle_end
|
||
|
|
||
|
BoxLayout:
|
||
|
id: left_actions
|
||
|
orientation: 'horizontal'
|
||
|
size_hint_x: None
|
||
|
padding: [0, (self.height - dp(48))/2]
|
||
|
|
||
|
BoxLayout:
|
||
|
padding: dp(12), 0
|
||
|
|
||
|
MDLabel:
|
||
|
id: label_title
|
||
|
font_style: 'H6'
|
||
|
opposite_colors: root.opposite_colors
|
||
|
theme_text_color: 'Custom'
|
||
|
text_color: root.specific_text_color
|
||
|
text: root.title
|
||
|
shorten: True
|
||
|
shorten_from: 'right'
|
||
|
halign: root.anchor_title
|
||
|
|
||
|
BoxLayout:
|
||
|
id: right_actions
|
||
|
orientation: 'horizontal'
|
||
|
size_hint_x: None
|
||
|
padding: [0, (self.height - dp(48)) / 2]
|
||
|
"""
|
||
|
)
|
||
|
|
||
|
|
||
|
class MDActionBottomAppBarButton(MDFloatingActionButton):
|
||
|
_scale_x = NumericProperty(1)
|
||
|
_scale_y = NumericProperty(1)
|
||
|
|
||
|
|
||
|
class MDToolbar(
|
||
|
ThemableBehavior,
|
||
|
RectangularElevationBehavior,
|
||
|
SpecificBackgroundColorBehavior,
|
||
|
BoxLayout,
|
||
|
):
|
||
|
"""
|
||
|
:Events:
|
||
|
`on_action_button`
|
||
|
Method for the button used for the :class:`~MDBottomAppBar` class.
|
||
|
"""
|
||
|
|
||
|
elevation = NumericProperty(6)
|
||
|
"""
|
||
|
Elevation value.
|
||
|
|
||
|
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
|
||
|
and defaults to `6`.
|
||
|
"""
|
||
|
|
||
|
left_action_items = ListProperty()
|
||
|
"""The icons on the left of the toolbar.
|
||
|
To add one, append a list like the following:
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
left_action_items: [`'icon_name'`, callback]
|
||
|
|
||
|
where `'icon_name'` is a string that corresponds to an icon definition and
|
||
|
``callback`` is the function called on a touch release event.
|
||
|
|
||
|
:attr:`left_action_items` is an :class:`~kivy.properties.ListProperty`
|
||
|
and defaults to `[]`.
|
||
|
"""
|
||
|
|
||
|
right_action_items = ListProperty()
|
||
|
"""The icons on the left of the toolbar.
|
||
|
Works the same way as :attr:`left_action_items`.
|
||
|
|
||
|
:attr:`right_action_items` is an :class:`~kivy.properties.ListProperty`
|
||
|
and defaults to `[]`.
|
||
|
"""
|
||
|
|
||
|
title = StringProperty()
|
||
|
"""Text toolbar.
|
||
|
|
||
|
:attr:`title` is an :class:`~kivy.properties.StringProperty`
|
||
|
and defaults to `''`.
|
||
|
"""
|
||
|
|
||
|
md_bg_color = ListProperty([0, 0, 0, 0])
|
||
|
"""Color toolbar.
|
||
|
|
||
|
:attr:`md_bg_color` is an :class:`~kivy.properties.ListProperty`
|
||
|
and defaults to `[0, 0, 0, 0]`.
|
||
|
"""
|
||
|
|
||
|
anchor_title = OptionProperty("left", options=["left", "center", "right"])
|
||
|
"""Position toolbar title.
|
||
|
Available options are: `'left'`, `'center'`, `'right'`.
|
||
|
|
||
|
:attr:`anchor_title` is an :class:`~kivy.properties.OptionProperty`
|
||
|
and defaults to `'left'`.
|
||
|
"""
|
||
|
|
||
|
mode = OptionProperty(
|
||
|
"center", options=["free-end", "free-center", "end", "center"]
|
||
|
)
|
||
|
"""Floating button position. Only for :class:`~MDBottomAppBar` class.
|
||
|
Available options are: `'free-end'`, `'free-center'`, `'end'`, `'center'`.
|
||
|
|
||
|
:attr:`mode` is an :class:`~kivy.properties.OptionProperty`
|
||
|
and defaults to `'center'`.
|
||
|
"""
|
||
|
|
||
|
round = NumericProperty("10dp")
|
||
|
"""
|
||
|
Rounding the corners at the notch for a button.
|
||
|
Onle for :class:`~MDBottomAppBar` class.
|
||
|
|
||
|
:attr:`round` is an :class:`~kivy.properties.NumericProperty`
|
||
|
and defaults to `'10dp'`.
|
||
|
"""
|
||
|
|
||
|
icon = StringProperty("android")
|
||
|
"""
|
||
|
Floating button. Onle for :class:`~MDBottomAppBar` class.
|
||
|
|
||
|
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
|
||
|
and defaults to `'android'`.
|
||
|
"""
|
||
|
|
||
|
icon_color = ListProperty()
|
||
|
"""
|
||
|
Color action button. Onle for :class:`~MDBottomAppBar` class.
|
||
|
|
||
|
:attr:`icon_color` is an :class:`~kivy.properties.ListProperty`
|
||
|
and defaults to `[]`.
|
||
|
"""
|
||
|
|
||
|
type = OptionProperty("top", options=["top", "bottom"])
|
||
|
"""
|
||
|
When using the :class:`~MDBottomAppBar` class, the parameter ``type``
|
||
|
must be set to `'bottom'`:
|
||
|
|
||
|
.. code-block:: kv
|
||
|
|
||
|
MDBottomAppBar:
|
||
|
|
||
|
MDToolbar:
|
||
|
type: "bottom"
|
||
|
|
||
|
Available options are: `'top'`, `'bottom'`.
|
||
|
|
||
|
:attr:`type` is an :class:`~kivy.properties.OptionProperty`
|
||
|
and defaults to `'top'`.
|
||
|
"""
|
||
|
|
||
|
_shift = NumericProperty("3.5dp")
|
||
|
_angle_start = NumericProperty(90)
|
||
|
_angle_end = NumericProperty(270)
|
||
|
|
||
|
def __init__(self, **kwargs):
|
||
|
self.action_button = MDActionBottomAppBarButton()
|
||
|
super().__init__(**kwargs)
|
||
|
self.register_event_type("on_action_button")
|
||
|
self.action_button.bind(
|
||
|
on_release=lambda x: self.dispatch("on_action_button")
|
||
|
)
|
||
|
self.action_button.x = Window.width / 2 - self.action_button.width / 2
|
||
|
self.action_button.y = (
|
||
|
(self.center[1] - self.height / 2)
|
||
|
+ self.theme_cls.standard_increment / 2
|
||
|
+ self._shift
|
||
|
)
|
||
|
if not self.icon_color:
|
||
|
self.icon_color = self.theme_cls.primary_color
|
||
|
Window.bind(on_resize=self._on_resize)
|
||
|
self.bind(specific_text_color=self.update_action_bar_text_colors)
|
||
|
Clock.schedule_once(
|
||
|
lambda x: self.on_left_action_items(0, self.left_action_items)
|
||
|
)
|
||
|
Clock.schedule_once(
|
||
|
lambda x: self.on_right_action_items(0, self.right_action_items)
|
||
|
)
|
||
|
|
||
|
def on_action_button(self, *args):
|
||
|
pass
|
||
|
|
||
|
def on_md_bg_color(self, instance, value):
|
||
|
if self.type == "bottom":
|
||
|
self.md_bg_color = [0, 0, 0, 0]
|
||
|
|
||
|
def on_left_action_items(self, instance, value):
|
||
|
self.update_action_bar(self.ids["left_actions"], value)
|
||
|
|
||
|
def on_right_action_items(self, instance, value):
|
||
|
self.update_action_bar(self.ids["right_actions"], value)
|
||
|
|
||
|
def update_action_bar(self, action_bar, action_bar_items):
|
||
|
action_bar.clear_widgets()
|
||
|
new_width = 0
|
||
|
for item in action_bar_items:
|
||
|
new_width += dp(48)
|
||
|
action_bar.add_widget(
|
||
|
MDIconButton(
|
||
|
icon=item[0],
|
||
|
on_release=item[1],
|
||
|
opposite_colors=True,
|
||
|
text_color=self.specific_text_color,
|
||
|
theme_text_color="Custom",
|
||
|
)
|
||
|
)
|
||
|
action_bar.width = new_width
|
||
|
|
||
|
def update_action_bar_text_colors(self, instance, value):
|
||
|
for child in self.ids["left_actions"].children:
|
||
|
child.text_color = self.specific_text_color
|
||
|
for child in self.ids["right_actions"].children:
|
||
|
child.text_color = self.specific_text_color
|
||
|
|
||
|
def _on_resize(self, instance, width, height):
|
||
|
if self.mode == "center":
|
||
|
self.action_button.x = width / 2 - self.action_button.width / 2
|
||
|
else:
|
||
|
self.action_button.x = width - self.action_button.width * 2
|
||
|
|
||
|
def on_icon(self, instance, value):
|
||
|
self.action_button.icon = value
|
||
|
|
||
|
def on_icon_color(self, instance, value):
|
||
|
self.action_button.md_bg_color = value
|
||
|
|
||
|
def on_mode(self, instance, value):
|
||
|
def set_button_pos(*args):
|
||
|
self.action_button.x = x
|
||
|
self.action_button.y = y
|
||
|
self.action_button._hard_shadow_size = (0, 0)
|
||
|
self.action_button._soft_shadow_size = (0, 0)
|
||
|
anim = Animation(_scale_x=1, _scale_y=1, d=0.05)
|
||
|
anim.bind(on_complete=self.set_shadow)
|
||
|
anim.start(self.action_button)
|
||
|
|
||
|
if value == "center":
|
||
|
self.set_notch()
|
||
|
x = Window.width / 2 - self.action_button.width / 2
|
||
|
y = (
|
||
|
(self.center[1] - self.height / 2)
|
||
|
+ self.theme_cls.standard_increment / 2
|
||
|
+ self._shift
|
||
|
)
|
||
|
elif value == "end":
|
||
|
|
||
|
self.set_notch()
|
||
|
x = Window.width - self.action_button.width * 2
|
||
|
y = (
|
||
|
(self.center[1] - self.height / 2)
|
||
|
+ self.theme_cls.standard_increment / 2
|
||
|
+ self._shift
|
||
|
)
|
||
|
self.right_action_items = []
|
||
|
elif value == "free-end":
|
||
|
self.remove_notch()
|
||
|
x = Window.width - self.action_button.width - dp(10)
|
||
|
y = self.action_button.height + self.action_button.height / 2
|
||
|
elif value == "free-center":
|
||
|
self.remove_notch()
|
||
|
x = Window.width / 2 - self.action_button.width / 2
|
||
|
y = self.action_button.height + self.action_button.height / 2
|
||
|
self.remove_shadow()
|
||
|
anim = Animation(_scale_x=0, _scale_y=0, d=0.05)
|
||
|
anim.bind(on_complete=set_button_pos)
|
||
|
anim.start(self.action_button)
|
||
|
|
||
|
def remove_notch(self):
|
||
|
self._angle_start = 0
|
||
|
self._angle_end = 0
|
||
|
self.round = 0
|
||
|
self._shift = 0
|
||
|
|
||
|
def set_notch(self):
|
||
|
self._angle_start = 90
|
||
|
self._angle_end = 270
|
||
|
self.round = dp(10)
|
||
|
self._shift = dp(3.5)
|
||
|
|
||
|
def remove_shadow(self):
|
||
|
self.action_button._hard_shadow_size = (0, 0)
|
||
|
self.action_button._soft_shadow_size = (0, 0)
|
||
|
|
||
|
def set_shadow(self, *args):
|
||
|
self.action_button._hard_shadow_size = (dp(112), dp(112))
|
||
|
self.action_button._soft_shadow_size = (dp(112), dp(112))
|
||
|
|
||
|
|
||
|
class MDBottomAppBar(FloatLayout):
|
||
|
def __init__(self, **kwargs):
|
||
|
super().__init__(**kwargs)
|
||
|
self.size_hint_y = None
|
||
|
|
||
|
def add_widget(self, widget, index=0, canvas=None):
|
||
|
if widget.__class__ is MDToolbar:
|
||
|
super().add_widget(widget)
|
||
|
return super().add_widget(widget.action_button)
|