304 lines
8.9 KiB
Python
304 lines
8.9 KiB
Python
|
"""
|
||
|
Behaviors/Elevation
|
||
|
===================
|
||
|
|
||
|
.. rubric:: Classes implements a circular and rectangular elevation effects.
|
||
|
|
||
|
To create a widget with rectangular or circular elevation effect,
|
||
|
you must create a new class that inherits from the
|
||
|
:class:`~RectangularElevationBehavior` or :class:`~CircularElevationBehavior`
|
||
|
class.
|
||
|
|
||
|
For example, let's create an button with a rectangular elevation effect:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
from kivy.lang import Builder
|
||
|
from kivy.uix.behaviors import ButtonBehavior
|
||
|
|
||
|
from kivymd.app import MDApp
|
||
|
from kivymd.uix.behaviors import (
|
||
|
RectangularRippleBehavior,
|
||
|
BackgroundColorBehavior,
|
||
|
RectangularElevationBehavior,
|
||
|
)
|
||
|
|
||
|
KV = '''
|
||
|
<RectangularElevationButton>:
|
||
|
size_hint: None, None
|
||
|
size: "250dp", "50dp"
|
||
|
|
||
|
|
||
|
Screen:
|
||
|
|
||
|
# With elevation effect
|
||
|
RectangularElevationButton:
|
||
|
pos_hint: {"center_x": .5, "center_y": .6}
|
||
|
elevation: 11
|
||
|
|
||
|
# Without elevation effect
|
||
|
RectangularElevationButton:
|
||
|
pos_hint: {"center_x": .5, "center_y": .4}
|
||
|
'''
|
||
|
|
||
|
|
||
|
class RectangularElevationButton(
|
||
|
RectangularRippleBehavior,
|
||
|
RectangularElevationBehavior,
|
||
|
ButtonBehavior,
|
||
|
BackgroundColorBehavior,
|
||
|
):
|
||
|
md_bg_color = [0, 0, 1, 1]
|
||
|
|
||
|
|
||
|
class Example(MDApp):
|
||
|
def build(self):
|
||
|
return Builder.load_string(KV)
|
||
|
|
||
|
|
||
|
Example().run()
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/rectangular-elevation-effect.gif
|
||
|
:align: center
|
||
|
|
||
|
Similarly, create a button with a circular elevation effect:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
from kivy.lang import Builder
|
||
|
from kivy.uix.image import Image
|
||
|
from kivy.uix.behaviors import ButtonBehavior
|
||
|
|
||
|
from kivymd.app import MDApp
|
||
|
from kivymd.uix.behaviors import (
|
||
|
CircularRippleBehavior,
|
||
|
CircularElevationBehavior,
|
||
|
)
|
||
|
|
||
|
KV = '''
|
||
|
#:import images_path kivymd.images_path
|
||
|
|
||
|
|
||
|
<CircularElevationButton>:
|
||
|
size_hint: None, None
|
||
|
size: "100dp", "100dp"
|
||
|
source: f"{images_path}/kivymd.png"
|
||
|
|
||
|
|
||
|
Screen:
|
||
|
|
||
|
# With elevation effect
|
||
|
CircularElevationButton:
|
||
|
pos_hint: {"center_x": .5, "center_y": .6}
|
||
|
elevation: 5
|
||
|
|
||
|
# Without elevation effect
|
||
|
CircularElevationButton:
|
||
|
pos_hint: {"center_x": .5, "center_y": .4}
|
||
|
elevation: 0
|
||
|
'''
|
||
|
|
||
|
|
||
|
class CircularElevationButton(
|
||
|
CircularRippleBehavior,
|
||
|
CircularElevationBehavior,
|
||
|
ButtonBehavior,
|
||
|
Image,
|
||
|
):
|
||
|
md_bg_color = [0, 0, 1, 1]
|
||
|
|
||
|
|
||
|
class Example(MDApp):
|
||
|
def build(self):
|
||
|
return Builder.load_string(KV)
|
||
|
|
||
|
|
||
|
Example().run()
|
||
|
|
||
|
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/circular-elevation-effect.gif
|
||
|
:align: center
|
||
|
"""
|
||
|
|
||
|
__all__ = (
|
||
|
"CommonElevationBehavior",
|
||
|
"RectangularElevationBehavior",
|
||
|
"CircularElevationBehavior",
|
||
|
)
|
||
|
|
||
|
from kivy.lang import Builder
|
||
|
from kivy.metrics import dp
|
||
|
from kivy.properties import ListProperty, NumericProperty, ObjectProperty
|
||
|
|
||
|
from kivymd.app import MDApp
|
||
|
|
||
|
Builder.load_string(
|
||
|
"""
|
||
|
<RectangularElevationBehavior>
|
||
|
canvas.before:
|
||
|
Color:
|
||
|
a: self._soft_shadow_a
|
||
|
Rectangle:
|
||
|
texture: self._soft_shadow_texture
|
||
|
size: self._soft_shadow_size
|
||
|
pos: self._soft_shadow_pos
|
||
|
Color:
|
||
|
a: self._hard_shadow_a
|
||
|
Rectangle:
|
||
|
texture: self._hard_shadow_texture
|
||
|
size: self._hard_shadow_size
|
||
|
pos: self._hard_shadow_pos
|
||
|
Color:
|
||
|
a: 1
|
||
|
|
||
|
|
||
|
<CircularElevationBehavior>
|
||
|
canvas.before:
|
||
|
Color:
|
||
|
a: self._soft_shadow_a
|
||
|
Rectangle:
|
||
|
texture: self._soft_shadow_texture
|
||
|
size: self._soft_shadow_size
|
||
|
pos: self._soft_shadow_pos
|
||
|
Color:
|
||
|
a: self._hard_shadow_a
|
||
|
Rectangle:
|
||
|
texture: self._hard_shadow_texture
|
||
|
size: self._hard_shadow_size
|
||
|
pos: self._hard_shadow_pos
|
||
|
Color:
|
||
|
a: 1
|
||
|
"""
|
||
|
)
|
||
|
|
||
|
|
||
|
class CommonElevationBehavior(object):
|
||
|
"""Common base class for rectangular and circular elevation behavior."""
|
||
|
|
||
|
elevation = NumericProperty(1)
|
||
|
"""
|
||
|
Elevation value.
|
||
|
|
||
|
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
|
||
|
and defaults to 1.
|
||
|
"""
|
||
|
|
||
|
_elevation = NumericProperty(0)
|
||
|
_soft_shadow_texture = ObjectProperty()
|
||
|
_soft_shadow_size = ListProperty((0, 0))
|
||
|
_soft_shadow_pos = ListProperty((0, 0))
|
||
|
_soft_shadow_a = NumericProperty(0)
|
||
|
_hard_shadow_texture = ObjectProperty()
|
||
|
_hard_shadow_size = ListProperty((0, 0))
|
||
|
_hard_shadow_pos = ListProperty((0, 0))
|
||
|
_hard_shadow_a = NumericProperty(0)
|
||
|
|
||
|
def __init__(self, **kwargs):
|
||
|
self.bind(
|
||
|
elevation=self._update_elevation,
|
||
|
pos=self._update_shadow,
|
||
|
size=self._update_shadow,
|
||
|
)
|
||
|
super().__init__(**kwargs)
|
||
|
|
||
|
def _update_shadow(self, *args):
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def _update_elevation(self, instance, value):
|
||
|
if not self._elevation:
|
||
|
self._elevation = value
|
||
|
self._update_shadow(instance, value)
|
||
|
|
||
|
|
||
|
class RectangularElevationBehavior(CommonElevationBehavior):
|
||
|
"""Base class for rectangular elevation behavior.
|
||
|
Controls the size and position of the shadow."""
|
||
|
|
||
|
def _update_shadow(self, *args):
|
||
|
if self._elevation > 0:
|
||
|
# Set shadow size.
|
||
|
ratio = self.width / (self.height if self.height != 0 else 1)
|
||
|
if -2 < ratio < 2:
|
||
|
self._shadow = MDApp.get_running_app().theme_cls.quad_shadow
|
||
|
width = soft_width = self.width * 1.9
|
||
|
height = soft_height = self.height * 1.9
|
||
|
elif ratio <= -2:
|
||
|
self._shadow = MDApp.get_running_app().theme_cls.rec_st_shadow
|
||
|
ratio = abs(ratio)
|
||
|
if ratio > 5:
|
||
|
ratio = ratio * 22
|
||
|
else:
|
||
|
ratio = ratio * 11.5
|
||
|
width = soft_width = self.width * 1.9
|
||
|
height = self.height + dp(ratio)
|
||
|
soft_height = (
|
||
|
self.height + dp(ratio) + dp(self._elevation) * 0.5
|
||
|
)
|
||
|
else:
|
||
|
self._shadow = MDApp.get_running_app().theme_cls.quad_shadow
|
||
|
width = soft_width = self.width * 1.8
|
||
|
height = soft_height = self.height * 1.8
|
||
|
|
||
|
self._soft_shadow_size = (soft_width, soft_height)
|
||
|
self._hard_shadow_size = (width, height)
|
||
|
# Set ``soft_shadow`` parameters.
|
||
|
self._soft_shadow_pos = (
|
||
|
self.center_x - soft_width / 2,
|
||
|
self.center_y
|
||
|
- soft_height / 2
|
||
|
- dp(0.1 * 1.5 ** self._elevation),
|
||
|
)
|
||
|
self._soft_shadow_a = 0.1 * 1.1 ** self._elevation
|
||
|
self._soft_shadow_texture = self._shadow.textures[
|
||
|
str(int(round(self._elevation)))
|
||
|
]
|
||
|
# Set ``hard_shadow`` parameters.
|
||
|
self._hard_shadow_pos = (
|
||
|
self.center_x - width / 2,
|
||
|
self.center_y - height / 2 - dp(0.5 * 1.18 ** self._elevation),
|
||
|
)
|
||
|
self._hard_shadow_a = 0.4 * 0.9 ** self._elevation
|
||
|
self._hard_shadow_texture = self._shadow.textures[
|
||
|
str(int(round(self._elevation)))
|
||
|
]
|
||
|
else:
|
||
|
self._soft_shadow_a = 0
|
||
|
self._hard_shadow_a = 0
|
||
|
|
||
|
|
||
|
class CircularElevationBehavior(CommonElevationBehavior):
|
||
|
"""Base class for circular elevation behavior.
|
||
|
Controls the size and position of the shadow."""
|
||
|
|
||
|
def __init__(self, **kwargs):
|
||
|
super().__init__(**kwargs)
|
||
|
self._shadow = MDApp.get_running_app().theme_cls.round_shadow
|
||
|
|
||
|
def _update_shadow(self, *args):
|
||
|
if self.elevation > 0:
|
||
|
# Set shadow size.
|
||
|
width = self.width * 2
|
||
|
height = self.height * 2
|
||
|
|
||
|
x = self.center_x - width / 2
|
||
|
self._soft_shadow_size = (width, height)
|
||
|
self._hard_shadow_size = (width, height)
|
||
|
# Set ``soft_shadow`` parameters.
|
||
|
y = self.center_y - height / 2 - dp(0.1 * 1.5 ** self._elevation)
|
||
|
self._soft_shadow_pos = (x, y)
|
||
|
self._soft_shadow_a = 0.1 * 1.1 ** self._elevation
|
||
|
if hasattr(self, "_shadow"):
|
||
|
self._soft_shadow_texture = self._shadow.textures[
|
||
|
str(int(round(self._elevation)))
|
||
|
]
|
||
|
# Set ``hard_shadow`` parameters.
|
||
|
y = self.center_y - height / 2 - dp(0.5 * 1.18 ** self._elevation)
|
||
|
self._hard_shadow_pos = (x, y)
|
||
|
self._hard_shadow_a = 0.4 * 0.9 ** self._elevation
|
||
|
if hasattr(self, "_shadow"):
|
||
|
self._hard_shadow_texture = self._shadow.textures[
|
||
|
str(int(round(self._elevation)))
|
||
|
]
|
||
|
else:
|
||
|
self._soft_shadow_a = 0
|
||
|
self._hard_shadow_a = 0
|