170 lines
6.7 KiB
Python
170 lines
6.7 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
from kivy.properties import ListProperty, NumericProperty, StringProperty, \
|
||
|
BooleanProperty
|
||
|
from kivy.animation import Animation
|
||
|
from kivy.graphics import Color, Ellipse, StencilPush, StencilPop, \
|
||
|
StencilUse, StencilUnUse, Rectangle
|
||
|
|
||
|
|
||
|
class CommonRipple(object):
|
||
|
ripple_rad = NumericProperty()
|
||
|
ripple_rad_default = NumericProperty(1)
|
||
|
ripple_post = ListProperty()
|
||
|
ripple_color = ListProperty()
|
||
|
ripple_alpha = NumericProperty(.5)
|
||
|
ripple_scale = NumericProperty(None)
|
||
|
ripple_duration_in_fast = NumericProperty(.3)
|
||
|
# FIXME: These speeds should be calculated based on widget size in dp
|
||
|
ripple_duration_in_slow = NumericProperty(2)
|
||
|
ripple_duration_out = NumericProperty(.3)
|
||
|
ripple_func_in = StringProperty('out_quad')
|
||
|
ripple_func_out = StringProperty('out_quad')
|
||
|
|
||
|
doing_ripple = BooleanProperty(False)
|
||
|
finishing_ripple = BooleanProperty(False)
|
||
|
fading_out = BooleanProperty(False)
|
||
|
|
||
|
def on_touch_down(self, touch):
|
||
|
if touch.is_mouse_scrolling:
|
||
|
return False
|
||
|
if not self.collide_point(touch.x, touch.y):
|
||
|
return False
|
||
|
|
||
|
if not self.disabled:
|
||
|
if self.doing_ripple:
|
||
|
Animation.cancel_all(self, 'ripple_rad', 'ripple_color',
|
||
|
'rect_color')
|
||
|
self.anim_complete()
|
||
|
self.ripple_rad = self.ripple_rad_default
|
||
|
self.ripple_pos = (touch.x, touch.y)
|
||
|
|
||
|
if self.ripple_color != []:
|
||
|
pass
|
||
|
elif hasattr(self, 'theme_cls'):
|
||
|
self.ripple_color = self.theme_cls.ripple_color
|
||
|
else:
|
||
|
# If no theme, set Grey 300
|
||
|
self.ripple_color = [0.8784313725490196, 0.8784313725490196,
|
||
|
0.8784313725490196, self.ripple_alpha]
|
||
|
self.ripple_color[3] = self.ripple_alpha
|
||
|
|
||
|
self.lay_canvas_instructions()
|
||
|
self.finish_rad = max(self.width, self.height) * self.ripple_scale
|
||
|
self.start_ripple()
|
||
|
return super(CommonRipple, self).on_touch_down(touch)
|
||
|
|
||
|
def lay_canvas_instructions(self):
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def on_touch_move(self, touch, *args):
|
||
|
if not self.collide_point(touch.x, touch.y):
|
||
|
if not self.finishing_ripple and self.doing_ripple:
|
||
|
self.finish_ripple()
|
||
|
return super(CommonRipple, self).on_touch_move(touch, *args)
|
||
|
|
||
|
def on_touch_up(self, touch):
|
||
|
if self.collide_point(touch.x, touch.y) and self.doing_ripple:
|
||
|
self.finish_ripple()
|
||
|
return super(CommonRipple, self).on_touch_up(touch)
|
||
|
|
||
|
def start_ripple(self):
|
||
|
if not self.doing_ripple:
|
||
|
anim = Animation(
|
||
|
ripple_rad=self.finish_rad,
|
||
|
t='linear',
|
||
|
duration=self.ripple_duration_in_slow)
|
||
|
anim.bind(on_complete=self.fade_out)
|
||
|
self.doing_ripple = True
|
||
|
anim.start(self)
|
||
|
|
||
|
def _set_ellipse(self, instance, value):
|
||
|
self.ellipse.size = (self.ripple_rad, self.ripple_rad)
|
||
|
|
||
|
# Adjust ellipse pos here
|
||
|
|
||
|
def _set_color(self, instance, value):
|
||
|
self.col_instruction.a = value[3]
|
||
|
|
||
|
def finish_ripple(self):
|
||
|
if self.doing_ripple and not self.finishing_ripple:
|
||
|
Animation.cancel_all(self, 'ripple_rad')
|
||
|
anim = Animation(ripple_rad=self.finish_rad,
|
||
|
t=self.ripple_func_in,
|
||
|
duration=self.ripple_duration_in_fast)
|
||
|
anim.bind(on_complete=self.fade_out)
|
||
|
self.finishing_ripple = True
|
||
|
anim.start(self)
|
||
|
|
||
|
def fade_out(self, *args):
|
||
|
rc = self.ripple_color
|
||
|
if not self.fading_out:
|
||
|
Animation.cancel_all(self, 'ripple_color')
|
||
|
anim = Animation(ripple_color=[rc[0], rc[1], rc[2], 0.],
|
||
|
t=self.ripple_func_out,
|
||
|
duration=self.ripple_duration_out)
|
||
|
anim.bind(on_complete=self.anim_complete)
|
||
|
self.fading_out = True
|
||
|
anim.start(self)
|
||
|
|
||
|
def anim_complete(self, *args):
|
||
|
self.doing_ripple = False
|
||
|
self.finishing_ripple = False
|
||
|
self.fading_out = False
|
||
|
self.canvas.after.clear()
|
||
|
|
||
|
|
||
|
class RectangularRippleBehavior(CommonRipple):
|
||
|
ripple_scale = NumericProperty(2.75)
|
||
|
|
||
|
def lay_canvas_instructions(self):
|
||
|
with self.canvas.after:
|
||
|
StencilPush()
|
||
|
Rectangle(pos=self.pos, size=self.size)
|
||
|
StencilUse()
|
||
|
self.col_instruction = Color(rgba=self.ripple_color)
|
||
|
self.ellipse = \
|
||
|
Ellipse(size=(self.ripple_rad, self.ripple_rad),
|
||
|
pos=(self.ripple_pos[0] - self.ripple_rad / 2.,
|
||
|
self.ripple_pos[1] - self.ripple_rad / 2.))
|
||
|
StencilUnUse()
|
||
|
Rectangle(pos=self.pos, size=self.size)
|
||
|
StencilPop()
|
||
|
self.bind(ripple_color=self._set_color,
|
||
|
ripple_rad=self._set_ellipse)
|
||
|
|
||
|
def _set_ellipse(self, instance, value):
|
||
|
super(RectangularRippleBehavior, self)._set_ellipse(instance, value)
|
||
|
self.ellipse.pos = (self.ripple_pos[0] - self.ripple_rad / 2.,
|
||
|
self.ripple_pos[1] - self.ripple_rad / 2.)
|
||
|
|
||
|
|
||
|
class CircularRippleBehavior(CommonRipple):
|
||
|
ripple_scale = NumericProperty(1)
|
||
|
|
||
|
def lay_canvas_instructions(self):
|
||
|
with self.canvas.after:
|
||
|
StencilPush()
|
||
|
self.stencil = Ellipse(size=(self.width * self.ripple_scale,
|
||
|
self.height * self.ripple_scale),
|
||
|
pos=(self.center_x - (
|
||
|
self.width * self.ripple_scale) / 2,
|
||
|
self.center_y - (
|
||
|
self.height * self.ripple_scale) / 2))
|
||
|
StencilUse()
|
||
|
self.col_instruction = Color(rgba=self.ripple_color)
|
||
|
self.ellipse = Ellipse(size=(self.ripple_rad, self.ripple_rad),
|
||
|
pos=(self.center_x - self.ripple_rad / 2.,
|
||
|
self.center_y - self.ripple_rad / 2.))
|
||
|
StencilUnUse()
|
||
|
Ellipse(pos=self.pos, size=self.size)
|
||
|
StencilPop()
|
||
|
self.bind(ripple_color=self._set_color,
|
||
|
ripple_rad=self._set_ellipse)
|
||
|
|
||
|
def _set_ellipse(self, instance, value):
|
||
|
super(CircularRippleBehavior, self)._set_ellipse(instance, value)
|
||
|
if self.ellipse.size[0] > self.width * .6 and not self.fading_out:
|
||
|
self.fade_out()
|
||
|
self.ellipse.pos = (self.center_x - self.ripple_rad / 2.,
|
||
|
self.center_y - self.ripple_rad / 2.)
|