1 from kivy.graphics import Color, Line
2 from kivy.uix.behaviors import ButtonBehavior
3 from kivy.uix.boxlayout import BoxLayout
4 from kivy.uix.button import Button
5 from kivy.uix.stacklayout import StackLayout
7 class Grabbable(BoxLayout):
8 def __init__(self, **kwargs):
9 super(Grabbable, self).__init__(**kwargs)
11 self.form_canvas = None
13 self.point_area = None
14 with self.canvas.after:
16 self.line = Line(points=[])
18 def point(self, touch=None):
20 side = self.x + self.width / 6
21 if touch.x > side * 5:
22 self.point_area = 'right'
23 self.line.points = [self.x + self.width + 5, self.y + 5, self.x + self.width + 5, self.y + self.height - 5]
25 self.point_area = 'left'
26 self.line.points = [self.x - 5, self.y + 5, self.x - 5, self.y + self.height - 5]
27 elif touch.y > self.y + self.height / 2:
28 self.point_area = 'top'
29 self.line.points = [self.x + 5, self.y + self.height + 5, self.x + self.width - 5, self.y + self.height + 5]
31 self.point_area = 'bottom'
32 self.line.points = [self.x + 5, self.y - 5, self.x + self.width - 5, self.y - 5]
35 self.point_area = None
38 def get_form_canvas(self):
40 while parent and not isinstance(parent, FormCanvas):
41 parent = parent.parent
45 for idx in range(len(self.parent.children)):
46 if self.parent.children[idx] is self:
52 self.form_canvas = self.get_form_canvas()
53 root_window = self.get_root_window()
54 self.parent.remove_widget(self)
55 self.size_hint = (None, None)
58 root_window.add_widget(self)
60 def attach(self, x, y):
62 self.parent.remove_widget(self)
63 self.height = self.form_canvas.widgets_height
64 self.size_hint = self.form_canvas.widgets_size_hint
66 for widget in self.form_canvas.walk(restrict=True):
67 if type(widget) is Grabbable and widget.collide_point(x, y):
68 idx = widget.get_index()
69 if widget.point_area == 'top':
70 widget.parent.add_widget(self, index=idx + 1)
71 elif widget.point_area == 'bottom':
72 widget.parent.add_widget(self, index=idx)
73 elif widget.point_area == 'left':
74 parent = widget.parent
75 box = parent.create_box()
76 parent.add_widget(box, index=idx)
78 parent.remove_widget(widget)
79 box.add_widget(widget)
81 parent = widget.parent
82 box = parent.create_box()
83 parent.add_widget(box, index=idx)
84 parent.remove_widget(widget)
85 box.add_widget(widget)
90 self.form_canvas.add_widget(self)
92 def on_touch_down(self, touch):
93 if self.collide_point(*touch.pos):
94 if touch.grab_current is None:
97 return super(Grabbable, self).on_touch_down(touch)
99 def on_touch_move(self, touch):
100 if touch.grab_current is self:
101 self.pos = [touch.x - self.width / 2, touch.y - self.height / 2]
103 if not self.detached:
106 if self.parent and self.parent != self.get_root_window():
107 if self.collide_point(touch.x, touch.y):
112 def on_touch_up(self, touch):
113 if touch.grab_current is self:
116 self.attach(touch.x, touch.y)
119 class FormCanvas(ButtonBehavior, StackLayout):
120 def __init__(self, *args, **kwargs):
121 super(FormCanvas, self).__init__(*args, **kwargs)
123 self.orientation = 'lr-tb'
124 self.padding = [10, 10, 10, 10]
125 self.spacing = [10, 10]
127 self.widgets_height = 40
128 self.widgets_size_hint = (1, None)
130 def on_press(self, *args):
133 height=self.widgets_height,
134 size_hint=self.widgets_size_hint
136 g.add_widget(Button(text=str(self.n)))
138 #print(self.export_to_kv(self))
140 def create_box(self):
142 orientation='horizontal',
143 height=self.widgets_height,
144 size_hint=self.widgets_size_hint,
145 spacing=self.spacing[0]
148 def export_to_kv(self, widget, indent=''):
150 orientation: '{orientation}'
153 """.format(orientation=self.orientation, padding=self.padding, spacing=self.spacing)
158 widgets = self.walk(restrict=True)
159 next(widgets) # the first widget is the FormCanvas
160 for widget in widgets:
161 if not type(widget) is Grabbable:
162 # Look for the widget position inside the tree
163 parent = widget.parent
164 if type(parent) is Grabbable:
165 parent = parent.parent
166 while not parent is stack[-1]:
170 kv += '{indent}{widget}:\n'.format(indent=indent*len(stack), widget=type(widget).__name__)
175 kv += """{indent}height: 40
176 {indent}size_hint: (1., None)
177 {indent}text: '{text}'
178 """.format(indent=indent*len(stack), text=widget.text)