reorganize files and bring code inline with current master
Conflicts: lib/simple_config.py
This commit is contained in:
257
gui/kivy/uix/drawer.py
Normal file
257
gui/kivy/uix/drawer.py
Normal file
@@ -0,0 +1,257 @@
|
||||
'''Drawer Widget to hold the main window and the menu/hidden section that
|
||||
can be swiped in from the left. This Menu would be only hidden in phone mode
|
||||
and visible in Tablet Mode.
|
||||
|
||||
This class is specifically in lined to save on start up speed(minimize i/o).
|
||||
'''
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import OptionProperty, NumericProperty, ObjectProperty
|
||||
from kivy.clock import Clock
|
||||
from kivy.lang import Builder
|
||||
|
||||
import gc
|
||||
|
||||
# delayed imports
|
||||
app = None
|
||||
|
||||
|
||||
class Drawer(Factory.RelativeLayout):
|
||||
'''Drawer Widget to hold the main window and the menu/hidden section that
|
||||
can be swiped in from the left. This Menu would be only hidden in phone mode
|
||||
and visible in Tablet Mode.
|
||||
|
||||
'''
|
||||
|
||||
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('9dp')
|
||||
'''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('9dp')
|
||||
'''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)
|
||||
''' This is the widget that is hidden in phone mode on the left side of
|
||||
drawer or displayed on the left of the overlay widget in tablet mode.
|
||||
|
||||
:attr:`hidden_widget` is a `ObjectProperty` defaults to None.
|
||||
'''
|
||||
|
||||
overlay_widget = ObjectProperty(None)
|
||||
'''This a pointer to the default widget that is overlayed either on top or
|
||||
to the right of the hidden widget.
|
||||
'''
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(Drawer, self).__init__(**kwargs)
|
||||
|
||||
self._triigger_gc = Clock.create_trigger(self._re_enable_gc, .2)
|
||||
|
||||
def toggle_drawer(self):
|
||||
if app.ui_mode[0] == 't':
|
||||
return
|
||||
Factory.Animation.cancel_all(self.overlay_widget)
|
||||
anim = Factory.Animation(x=self.hidden_widget.width
|
||||
if self.state in ('opening', 'closed') else 0,
|
||||
d=.1, t='linear')
|
||||
anim.bind(on_complete = self._complete_drawer_animation)
|
||||
anim.start(self.overlay_widget)
|
||||
|
||||
def _re_enable_gc(self, dt):
|
||||
global gc
|
||||
gc.enable()
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
if self.disabled:
|
||||
return
|
||||
|
||||
if not self.collide_point(*touch.pos):
|
||||
return
|
||||
|
||||
touch.grab(self)
|
||||
|
||||
# disable gc for smooth interaction
|
||||
# This is still not enough while wallet is synchronising
|
||||
# look into pausing all background tasks while ui interaction like this
|
||||
gc.disable()
|
||||
|
||||
global app
|
||||
if not app:
|
||||
app = App.get_running_app()
|
||||
|
||||
# skip on tablet mode
|
||||
if app.ui_mode[0] == 't':
|
||||
return super(Drawer, self).on_touch_down(touch)
|
||||
|
||||
state = self.state
|
||||
touch.ud['send_touch_down'] = False
|
||||
start = 0 #if state[0] == 'c' else self.hidden_widget.right
|
||||
drag_area = self.drag_area\
|
||||
if self.state[0] == 'c' else\
|
||||
(self.overlay_widget.x)
|
||||
|
||||
if touch.x < start or touch.x > drag_area:
|
||||
if self.state == 'open':
|
||||
self.toggle_drawer()
|
||||
return
|
||||
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):
|
||||
if not touch.grab_current is self:
|
||||
return
|
||||
self._touch = False
|
||||
# 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)
|
||||
|
||||
ov = self.overlay_widget
|
||||
ov.x=min(self.hidden_widget.width,
|
||||
max(ov.x + touch.dx*2, 0))
|
||||
|
||||
#_anim = Animation(x=x, duration=1/2, t='in_out_quart')
|
||||
#_anim.cancel_all(ov)
|
||||
#_anim.start(ov)
|
||||
|
||||
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.ungrab(self)
|
||||
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):
|
||||
if not touch.grab_current is self:
|
||||
return
|
||||
|
||||
self._triigger_gc()
|
||||
|
||||
touch.ungrab(self)
|
||||
touch.grab_current = None
|
||||
|
||||
# skip on tablet mode
|
||||
get = touch.ud.get
|
||||
if app.ui_mode[0] == 't':
|
||||
return super(Drawer, self).on_touch_up(touch)
|
||||
|
||||
self.old_x = [1, ] * 10
|
||||
self.speed = sum((
|
||||
(self.old_x[x + 1] - self.old_x[x]) for x in range(9))) / 9.
|
||||
|
||||
if get('send_touch_down', None):
|
||||
# touch up called before moving
|
||||
Clock.unschedule(self._change_touch_mode)
|
||||
self._touch = None
|
||||
Clock.schedule_once(
|
||||
lambda dt: super(Drawer, self).on_touch_down(touch))
|
||||
if get('in_drag_area', None):
|
||||
if abs(touch.x - touch.ox) < self.scroll_distance:
|
||||
anim_to = (0 if self.state[0] == 'c'
|
||||
else self.hidden_widget.width)
|
||||
Factory.Animation(x=anim_to, d=.1).start(self.overlay_widget)
|
||||
return
|
||||
touch.ud['in_drag_area'] = False
|
||||
if not get('send_touch_down', None):
|
||||
self.toggle_drawer()
|
||||
Clock.schedule_once(lambda dt: super(Drawer, self).on_touch_up(touch))
|
||||
|
||||
def _complete_drawer_animation(self, *args):
|
||||
self.state = 'open' if self.state in ('opening', 'closed') else 'closed'
|
||||
|
||||
def add_widget(self, widget, index=1):
|
||||
if not widget:
|
||||
return
|
||||
|
||||
iget = self.ids.get
|
||||
if not iget('hidden_widget') or not iget('overlay_widget'):
|
||||
super(Drawer, self).add_widget(widget)
|
||||
return
|
||||
|
||||
if not self.hidden_widget:
|
||||
self.hidden_widget = self.ids.hidden_widget
|
||||
if not self.overlay_widget:
|
||||
self.overlay_widget = self.ids.overlay_widget
|
||||
|
||||
if self.overlay_widget.children and self.hidden_widget.children:
|
||||
Logger.debug('Drawer: Accepts only two widgets. discarding rest')
|
||||
return
|
||||
|
||||
if not self.hidden_widget.children:
|
||||
self.hidden_widget.add_widget(widget)
|
||||
else:
|
||||
self.overlay_widget.add_widget(widget)
|
||||
widget.x = 0
|
||||
|
||||
def remove_widget(self, widget):
|
||||
if self.overlay_widget.children[0] == widget:
|
||||
self.overlay_widget.clear_widgets()
|
||||
return
|
||||
if widget == self.hidden_widget.children:
|
||||
self.hidden_widget.clear_widgets()
|
||||
return
|
||||
|
||||
def clear_widgets(self):
|
||||
self.overlay_widget.clear_widgets()
|
||||
self.hidden_widget.clear_widgets()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.app import runTouchApp
|
||||
from kivy.lang import Builder
|
||||
runTouchApp(Builder.load_string('''
|
||||
Drawer:
|
||||
Button:
|
||||
Button
|
||||
'''))
|
||||
Reference in New Issue
Block a user