Permalink
Browse files

first pass for the designer implementation.

  • Loading branch information...
1 parent 99e7f32 commit d1989523950dd51a6c795572194121366da4db1a @tito tito committed Sep 25, 2012
Showing with 336 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. 0 designer/__init__.py
  3. +71 −0 designer/app.py
  4. +35 −0 designer/common.py
  5. +78 −0 designer/designer.kv
  6. +96 −0 designer/playground.py
  7. +4 −0 designer/statusbar.py
  8. +46 −0 designer/toolbox.py
  9. +3 −0 main.py
View
@@ -0,0 +1,3 @@
+*.pyo
+*.pyc
+.*.swp
No changes.
View
@@ -0,0 +1,71 @@
+__all__ = ('DesignerApp', )
+
+import kivy
+kivy.require('1.4.1')
+from kivy.app import App
+from kivy.uix.floatlayout import FloatLayout
+from kivy.factory import Factory
+from kivy.properties import ObjectProperty
+from designer.playground import Playground, PlaygroundDragElement
+from designer.toolbox import Toolbox
+from designer.statusbar import StatusBar
+from designer.common import widgets
+
+
+class DesignerApp(App):
+
+ widget_focused = ObjectProperty()
+
+ def load_project(self, directory):
+ pass
+
+ def save_project(self, directory=None):
+ pass
+
+ def clear(self):
+ pass
+
+ def build(self):
+ self._widget_focused = None
+ self.root = FloatLayout()
+ self.playground = Playground()
+ self.toolbox = Toolbox()
+ self.statusbar = StatusBar()
+ self.root.add_widget(self.playground)
+ self.root.add_widget(self.toolbox)
+ self.root.add_widget(self.statusbar)
+
+ def create_draggable_element(self, widgetname, touch):
+ # create the element, and make it draggable until the touch is released
+ # search default args if exist
+ default_args = {}
+ for options in widgets:
+ if len(options) > 2:
+ default_args = options[2]
+ widget = getattr(Factory, widgetname)(**default_args)
+ container = PlaygroundDragElement(playground=self.playground)
+ container.add_widget(widget)
+ touch.grab(container)
+ self.root.add_widget(container)
+
+ def focus_widget(self, widget, *largs):
+ if self._widget_focused and (widget is None or self._widget_focused[0] != widget):
+ fwidget = self._widget_focused[0]
+ for instr in self._widget_focused[1:]:
+ fwidget.canvas.after.remove(instr)
+ self._widget_focused = []
+ self.widget_focused = widget
+ if not widget:
+ return
+ x, y = widget.pos
+ right, top = widget.right, widget.top
+ points = [x, y, right, y, right, top, x, top]
+ if self._widget_focused:
+ line = self._widget_focused[2]
+ line.points = points
+ else:
+ from kivy.graphics import Color, Line
+ with widget.canvas.after:
+ color = Color(.3, .9, .3)
+ line = Line(points=points, close=True, width=2.)
+ self._widget_focused = [widget, color, line]
View
@@ -0,0 +1,35 @@
+
+#: Describe the widgets to show in the toolbox, and anything else needed for the
+#: designer. The base is a list, because python dict don't preserve the order.
+#: The first field is the name used for Factory.<name>
+#: The second field represent a category name
+
+widgets = (
+ ('Label', 'base', {'text': 'A label'}),
+ ('Button', 'base', {'text': 'A button'}),
+ ('CheckBox', 'base'),
+ ('Image', 'base'),
+ ('Slider', 'base'),
+ ('ProgressBar', 'base'),
+ ('TextInput', 'base'),
+ ('ToggleButton', 'base'),
+ ('Switch', 'base'),
+ ('Video', 'base'),
+ ('GridLayout', 'layout', {'cols': 2}),
+ ('BoxLayout', 'layout'),
+ ('AnchorLayout', 'layout'),
+ ('StackLayout', 'layout'),
+ ('Bubble', 'complex'),
+ ('DropDown', 'complex'),
+ ('FileChooserListView', 'complex'),
+ ('FileChooserIconView', 'complex'),
+ ('Popup', 'complex'),
+ ('Spinner', 'complex'),
+ ('TabbedPanel', 'complex'),
+ ('VideoPlayer', 'complex'),
+ ('ScrollView', 'behavior'),
+ #('VKeybord', 'complex'),
+ #('Scatter', 'behavior'),
+ #('StencilView', 'behavior'),
+ #('ScreenManager', 'screenmanager'),
+)
View
@@ -0,0 +1,78 @@
+#:kivy 1.4.1
+
+<Playground>:
+ root: root_widget
+ do_scale: False
+ do_rotation: False
+ size_hint: None, None
+ size: 640, 480
+ pos: 155, 60
+ auto_bring_to_front: False
+ canvas:
+ Color:
+ rgb: 1, 1, 1
+ Line:
+ points: [0, 0, self.width, 0, self.width, self.height, 0, self.height]
+ width: 2.
+ close: True
+
+ FloatLayout:
+ id: root_widget
+ size: root.size
+
+<PlaygroundDragElement>:
+ size_hint: None, None
+ size: 100, 100
+ canvas:
+ Color:
+ rgb: (0.9, 0.9, 0.9) if self.can_place else (0.9, 0.1, 0.1)
+ Line:
+ points: [self.x, self.y, self.center_x - 20, self.y, self.center_x, self.y - 20, self.center_x + 20, self.y, self.right, self.y, self.right, self.top, self.x, self.top]
+ close: True
+ width: 2.
+
+ on_target: app.focus_widget(args[1])
+
+<Toolbox>:
+ widgets_list: widgets_list
+ size_hint: None, 1
+ width: 150
+
+ canvas:
+ Color:
+ rgb: 0.1, 0.1, 0.1
+ Rectangle:
+ pos: self.pos
+ size: self.size
+
+ ScrollView:
+ do_scroll_x: False
+ GridLayout:
+ id: widgets_list
+ cols: 1
+ size_hint_y: None
+ height: self.minimum_height
+
+<ToolboxCategory>:
+ bold: True
+ size_hint_y: None
+ height: '32pt'
+
+<ToolboxButton>:
+ size_hint_y: None
+ height: '32pt'
+ on_press_and_touch: app.create_draggable_element(self.text, args[1])
+
+<StatusBar>:
+ canvas:
+ Color:
+ rgb: 0.3, 0.3, 0.3
+ Rectangle:
+ pos: self.pos
+ size: self.size
+ size_hint_y: None
+ height: '20pt'
+
+ Label:
+ text: repr(app.widget_focused)
+ font_size: '10pt'
@@ -0,0 +1,96 @@
+from kivy.uix.scatter import ScatterPlane
+from kivy.uix.boxlayout import BoxLayout
+from kivy.uix.layout import Layout
+from kivy.properties import ObjectProperty, BooleanProperty
+from kivy.app import App
+from kivy.uix.filechooser import FileChooserListView, FileChooserIconView
+
+
+class PlaygroundDragElement(BoxLayout):
+
+ playground = ObjectProperty()
+ target = ObjectProperty(allownone=True)
+ can_place = BooleanProperty(False)
+
+ def on_touch_move(self, touch):
+ if touch.grab_current is self:
+ self.center_x = touch.x
+ self.y = touch.y + 20
+ self.target = self.playground.try_place_widget(
+ self.children[0], self.center_x, self.y - 20)
+ self.can_place = self.target is not None
+ return True
+
+ def on_touch_up(self, touch):
+ if touch.grab_current is self:
+ touch.ungrab(self)
+ self.target = self.playground.try_place_widget(
+ self.children[0], self.center_x, self.y - 20)
+ self.can_place = self.target is not None
+ if self.can_place:
+ child = self.children[0]
+ child.parent.remove_widget(child)
+ self.playground.place_widget(
+ child, self.center_x, self.y - 20)
+ self.parent.remove_widget(self)
+ return True
+
+
+class Playground(ScatterPlane):
+
+ root = ObjectProperty()
+ selection_mode = BooleanProperty(True)
+
+ def try_place_widget(self, widget, x, y):
+ x, y = self.to_local(x, y)
+ return self.find_target(x, y, self.root, widget)
+
+ def place_widget(self, widget, x, y):
+ x, y = self.to_local(x, y)
+ target = self.find_target(x, y, self.root, widget)
+ #wx, wy = target.to_widget(x, y)
+ #widget.pos = wx, wy
+ widget.pos = 0, 0
+ target.add_widget(widget)
+
+ def find_target(self, x, y, target, widget=None):
+ if not target.collide_point(x, y):
+ return None
+ x, y = target.to_local(x, y)
+ for child in target.children:
+ if not child.collide_point(x, y):
+ continue
+ if not self.allowed_target_for(child, widget):
+ continue
+ return self.find_target(x, y, child, widget)
+ return target
+
+ def allowed_target_for(self, target, widget):
+ # stop on complex widget
+ t = target if widget else target.parent
+ if isinstance(t, FileChooserListView):
+ return False
+ if isinstance(t, FileChooserIconView):
+ return False
+
+ # if we don't have widget, always return true
+ if widget is None:
+ return True
+
+ is_widget_layout = isinstance(widget, Layout)
+ is_target_layout = isinstance(target, Layout)
+ if is_widget_layout and is_target_layout:
+ return True
+ if is_target_layout:
+ return True
+ return False
+
+ def on_touch_down(self, touch):
+ if self.selection_mode:
+ if super(ScatterPlane, self).collide_point(*touch.pos):
+ x, y = self.to_local(*touch.pos)
+ target = self.find_target(x, y, self.root)
+ App.get_running_app().focus_widget(target)
+ return True
+ return super(Playground, self).on_touch_down(touch)
+
@@ -0,0 +1,4 @@
+from kivy.uix.boxlayout import BoxLayout
+
+class StatusBar(BoxLayout):
+ pass
View
@@ -0,0 +1,46 @@
+from kivy.uix.floatlayout import FloatLayout
+from kivy.uix.label import Label
+from kivy.uix.button import Button
+from kivy.properties import ObjectProperty
+from designer.common import widgets
+
+
+class ToolboxCategory(Label):
+ pass
+
+
+class ToolboxButton(Button):
+
+ def __init__(self, **kwargs):
+ self.register_event_type('on_press_and_touch')
+ super(ToolboxButton, self).__init__(**kwargs)
+
+ def on_touch_down(self, touch):
+ if self.collide_point(*touch.pos):
+ self.dispatch('on_press_and_touch', touch)
+ return super(ToolboxButton, self).on_touch_down(touch)
+
+ def on_press_and_touch(self, touch):
+ pass
+
+
+class Toolbox(FloatLayout):
+
+ widgets_list = ObjectProperty()
+ app = ObjectProperty()
+
+ def __init__(self, **kwargs):
+ super(Toolbox, self).__init__(**kwargs)
+ self.discover_widgets()
+
+ def discover_widgets(self):
+ # for now, don't do auto detection of widgets.
+ # just do manual discovery, and tagging.
+ categories = list(set([x[1] for x in widgets]))
+ for category in categories:
+ self.widgets_list.add_widget(ToolboxCategory(text=category))
+ for widget in widgets:
+ if widget[1] != category:
+ continue
+ self.widgets_list.add_widget(ToolboxButton(text=widget[0]))
+
View
@@ -0,0 +1,3 @@
+if __name__ == '__main__':
+ from designer.app import DesignerApp
+ DesignerApp().run()

0 comments on commit d198952

Please sign in to comment.