You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

173 lines
5.8 KiB

from kivy.uix.stencilview import StencilView
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import OptionProperty, NumericProperty, ObjectProperty
# delayed import
app = None
class Drawer(StencilView):
state = OptionProperty('closed',
options=('closed', 'open', 'opening', 'closing'))
'''This indicates the current state the drawer is in.
:attr:`state` is a `OptionProperty` defaults to `closed`. Can be one of
`closed`, `open`, `opening`, `closing`.
'''
scroll_timeout = NumericProperty(200)
'''Timeout allowed to trigger the :data:`scroll_distance`,
in milliseconds. If the user has not moved :data:`scroll_distance`
within the timeout, the scrolling will be disabled and the touch event
will go to the children.
:data:`scroll_timeout` is a :class:`~kivy.properties.NumericProperty`
and defaults to 200 (milliseconds)
'''
scroll_distance = NumericProperty('4dp')
'''Distance to move before scrolling the :class:`Drawer` in pixels.
As soon as the distance has been traveled, the :class:`Drawer` will
start to scroll, and no touch event will go to children.
It is advisable that you base this value on the dpi of your target
device's screen.
:data:`scroll_distance` is a :class:`~kivy.properties.NumericProperty`
and defaults to 20dp.
'''
drag_area = NumericProperty(.1)
'''The percentage of area on the left edge that triggers the opening of
the drawer. from 0-1
:attr:`drag_area` is a `NumericProperty` defaults to 2
'''
_hidden_widget = ObjectProperty(None)
_overlay_widget = ObjectProperty(None)
def __init__(self, **kwargs):
super(Drawer, self).__init__(**kwargs)
self.bind(pos=self._do_layout,
size=self._do_layout,
children=self._do_layout)
def _do_layout(self, instance, value):
if not self._hidden_widget or not self._overlay_widget:
return
self._overlay_widget.height = self._hidden_widget.height =\
self.height
def on_touch_down(self, touch):
if self.disabled:
return
global app
if not app:
from kivy.app import App
app = App.get_running_app()
# skip on tablet mode
if app.ui_mode[0] == 't':
return super(Drawer, self).on_touch_down(touch)
touch.ud['send_touch_down'] = False
drag_area = ((self.width * self.drag_area)
if self.state[0] == 'c' else
self._hidden_widget.width)
if touch.x > drag_area:
return super(Drawer, self).on_touch_down(touch)
self._touch = touch
Clock.schedule_once(self._change_touch_mode,
self.scroll_timeout/1000.)
touch.ud['in_drag_area'] = True
touch.ud['send_touch_down'] = True
return
def on_touch_move(self, touch):
global app
if not app:
from kivy.app import App
app = App.get_running_app()
# skip on tablet mode
if app.ui_mode[0] == 't':
return super(Drawer, self).on_touch_move(touch)
if not touch.ud.get('in_drag_area', None):
return super(Drawer, self).on_touch_move(touch)
self._overlay_widget.x = min(self._hidden_widget.width,
max(self._overlay_widget.x + touch.dx*2, 0))
if abs(touch.x - touch.ox) < self.scroll_distance:
return
touch.ud['send_touch_down'] = False
Clock.unschedule(self._change_touch_mode)
self._touch = None
self.state = 'opening' if touch.dx > 0 else 'closing'
touch.ox = touch.x
return
def _change_touch_mode(self, *args):
if not self._touch:
return
touch = self._touch
touch.ud['in_drag_area'] = False
touch.ud['send_touch_down'] = False
self._touch = None
super(Drawer, self).on_touch_down(touch)
return
def on_touch_up(self, touch):
# skip on tablet mode
if app.ui_mode[0] == 't':
return super(Drawer, self).on_touch_down(touch)
if touch.ud.get('send_touch_down', None):
Clock.unschedule(self._change_touch_mode)
Clock.schedule_once(
lambda dt: super(Drawer, self).on_touch_down(touch), -1)
if touch.ud.get('in_drag_area', None):
touch.ud['in_drag_area'] = False
Animation.cancel_all(self._overlay_widget)
anim = Animation(x=self._hidden_widget.width
if self.state[0] == 'o' else 0,
d=.1, t='linear')
anim.bind(on_complete = self._complete_drawer_animation)
anim.start(self._overlay_widget)
Clock.schedule_once(
lambda dt: super(Drawer, self).on_touch_up(touch), 0)
def _complete_drawer_animation(self, *args):
self.state = 'open' if self.state[0] == 'o' else 'closed'
def add_widget(self, widget, index=1):
if not widget:
return
children = self.children
len_children = len(children)
if len_children == 2:
Logger.debug('Drawer: No more than two widgets allowed')
return
super(Drawer, self).add_widget(widget)
if len_children == 0:
# first widget add it to the hidden/drawer section
self._hidden_widget = widget
return
# Second Widget
self._overlay_widget = widget
def remove_widget(self, widget):
super(Drawer, self).remove_widget(self)
if widget == self._hidden_widget:
self._hidden_widget = None
return
if widget == self._overlay_widget:
self._overlay_widget = None
return