Created
February 13, 2015 18:17
-
-
Save clayote/9f67025b178adccb6587 to your computer and use it in GitHub Desktop.
Abbreviated card.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from kivy.adapters.listadapter import ListAdapter | |
from kivy.lang import Builder | |
from kivy.logger import Logger | |
from kivy.properties import ( | |
BooleanProperty, | |
ListProperty, | |
NumericProperty, | |
ObjectProperty, | |
StringProperty | |
) | |
from kivy.uix.layout import Layout | |
from kivy.uix.floatlayout import FloatLayout | |
from kivy.uix.listview import ListView | |
class Card(FloatLayout): | |
dragging = BooleanProperty(False) | |
idx = NumericProperty() | |
bg_color = ListProperty([1, 1, 1, 1]) | |
text_color = ListProperty([0, 0, 0, 1]) | |
text = StringProperty('') | |
def get_kwargs(self): | |
return { | |
'bg_color': self.bg_color, | |
'text_color': self.text_color, | |
'text': self.text | |
} | |
def on_touch_down(self, touch): | |
if not self.collide_point(*touch.pos): | |
return | |
if 'card' in touch.ud: | |
return | |
touch.grab(self) | |
touch.ud['card'] = self.get_kwargs() | |
touch.ud['idx'] = self.idx | |
touch.ud['layout'] = self.parent | |
self.dragging = True | |
self._collide_x = touch.x - self.x | |
self._collide_y = touch.y - self.y | |
def on_touch_move(self, touch): | |
if not self.dragging: | |
return | |
self.pos = ( | |
touch.x - self._collide_x, | |
touch.y - self._collide_y | |
) | |
def on_touch_up(self, touch): | |
if not self.dragging: | |
return | |
self.dragging = False | |
class DeckView(ListView): | |
def on_touch_move(self, touch): | |
for child in self.children: | |
if child.collide_point(*touch.pos): | |
child.dispatch('on_touch_move', touch) | |
else: | |
child.insertion_point = None | |
class DeckLayout(Layout): | |
insertion_point = NumericProperty(None, allownone=True) | |
x_hint_step = NumericProperty(0.01) | |
y_hint_step = NumericProperty(-0.07) | |
adapter = ObjectProperty() | |
def point_before_card(self, card, x, y): | |
def ycmp(): | |
if self.y_hint_step == 0: | |
return False | |
elif self.y_hint_step > 0: | |
# stacking upward | |
return y < card.y | |
else: | |
# stacking downward | |
return y > card.top | |
if self.x_hint_step > 0: | |
# stacking to the right | |
if x < card.x: | |
return True | |
return ycmp() | |
elif self.x_hint_step == 0: | |
return ycmp() | |
else: | |
# stacking to the left | |
if x > card.right: | |
return True | |
return ycmp() | |
def point_after_card(self, card, x, y): | |
def ycmp(): | |
if self.y_hint_step == 0: | |
return False | |
elif self.y_hint_step > 0: | |
# stacking upward | |
return y > card.top | |
else: | |
# stacking downward | |
return y < card.y | |
if self.x_hint_step > 0: | |
# stacking to the right | |
if x > card.right: | |
return True | |
return ycmp() | |
elif self.x_hint_step == 0: | |
return ycmp() | |
else: | |
# stacking to the left | |
if x < card.x: | |
return True | |
return ycmp() | |
def do_layout(self, *args): | |
if self.size == [1, 1]: | |
return | |
cards = list(self.children) | |
inspt = self.insertion_point | |
# display a gap where the card would be inserted if you | |
# dropped it now | |
if inspt is not None and inspt > 0: | |
if inspt > len(cards): | |
cards.append(None) | |
else: | |
try: | |
dragidx = cards.index(next(c for c in cards if c.dragging)) | |
del cards[dragidx] | |
except StopIteration: | |
pass | |
cards.insert(len(cards)-inspt, None) | |
else: | |
try: | |
cards.remove(next(c for c in cards if c.dragging)) | |
except StopIteration: | |
pass | |
shw = 0.1 | |
shh = 0.2 | |
pos_hint = {'x': 0.05, 'top': 0.95} | |
w, h = self.size | |
x, y = self.pos | |
for child in cards: | |
if child is not None: | |
child.size = w * shw, h * shh | |
child.x = x + pos_hint['x'] * w | |
child.top = y + pos_hint['top'] * h | |
pos_hint['x'] += self.x_hint_step | |
pos_hint['top'] += self.y_hint_step | |
def on_parent(self, *args): | |
if self.parent is not None: | |
self._trigger_layout() | |
def on_children(self, *args): | |
self._trigger_layout() | |
def on_insertion_point(self, *args): | |
Logger.debug( | |
"DeckLayout: insertion point {}".format(self.insertion_point) | |
) | |
if self.insertion_point is not None: | |
self._trigger_layout() | |
def on_touch_move(self, touch): | |
if 'card' not in touch.ud: | |
Logger.debug( | |
"DeckLayout: no card, won't handle touch" | |
) | |
return | |
cards = [c for c in self.children if not c.dragging] | |
i = len(cards) | |
for card in cards: | |
if card.collide_point(*touch.pos): | |
if self.insertion_point != i: | |
self.insertion_point = i | |
return | |
i -= 1 | |
else: | |
# it seems like these return false positives sometimes?? | |
if self.insertion_point in (0, len(self.children)): | |
return | |
if self.point_before_card( | |
self.children[0], *touch.pos | |
): | |
self.insertion_point = len(self.children) | |
elif self.point_after_card( | |
self.children[-1], *touch.pos | |
): | |
self.insertion_point = 0 | |
def on_touch_up(self, touch): | |
if 'card' not in touch.ud: | |
return | |
kwargs = touch.ud['card'] | |
if self.insertion_point is not None and self.collide_point(*touch.pos): | |
del touch.ud['layout'].adapter.data[touch.ud['idx']] | |
self.adapter.data.insert(self.insertion_point, kwargs) | |
self.insertion_point = None | |
kv = """ | |
<Card>: | |
canvas: | |
Color: | |
rgba: root.bg_color | |
Rectangle: | |
pos: root.pos | |
size: root.size | |
Color: | |
rgba: [0, 0, 0, 1] | |
Line: | |
points: [root.x, root.y, root.right, root.y, root.right, root.top, root.x, root.top, root.x, root.y] | |
Color: | |
rgba: [1, 1, 1, 1] | |
Label: | |
color: root.text_color | |
text: root.text | |
pos: root.pos | |
text_size: root.size | |
<DeckView>: | |
container: container | |
DeckLayout: | |
id: container | |
adapter: root.adapter | |
pos: root.pos | |
size: root.size | |
""" | |
Builder.load_string(kv) | |
if __name__ == '__main__': | |
from kivy.base import runTouchApp | |
from kivy.core.window import Window | |
from kivy.modules import inspector | |
from kivy.uix.boxlayout import BoxLayout | |
data0 = [{'text_color': [0, 0, 0, 1], 'text': 'deck 0 card {}'.format(i)} for i in range(0, 9)] | |
data1 = [{'text': 'deck 1 card {}'.format(i)} for i in range(0, 9)] | |
def args_converter(i, kv): | |
kv['idx'] = i | |
return kv | |
adapter0 = ListAdapter(data=data0, cls=Card, args_converter=args_converter) | |
adapter1 = ListAdapter(data=data1, cls=Card, args_converter=args_converter) | |
layout = BoxLayout(orientation='vertical') | |
view0 = DeckView(adapter=adapter0) | |
view1 = DeckView(adapter=adapter1) | |
layout.add_widget(view0) | |
layout.add_widget(view1) | |
inspector.create_inspector(Window, layout) | |
runTouchApp(layout) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment