STYLE_EDITING = 3
STYLE_DRAGGING = 4
+STYLE_TO_CLASS = [
+ "normal"
+ "selected"
+ "hover"
+ "editing"
+ "dragging"
+]
+
+set_style_class = (args) ->
+ args.el.setAttribute 'class', "#{args.class} #{STYLE_TO_CLASS[args.style]}"
+
# json (compiled to javascript and minified) is ~8% smaller than the resulting xml
json_to_svg = (json) ->
for tag, attrs of json
if k is 'children'
for child in v
el.appendChild json_to_svg child
+ else if k is 'contents'
+ el.appendChild document.createTextNode v
else
el.setAttribute k, v
return el
+next_widget_id = 0
# public vars: x, y, width, height, el
-class Widget
+class Visible
+ # required args: svg
constructor: (args) ->
- @style = STYLE_NORMAL
- @x = args?.x ? 1
- @y = args?.y ? 1
- @width = args?.width ? 50
- @height = args?.height ? 34
- clone: ->
- return new Widget @
+ @id = next_widget_id
+ next_widget_id += 1
+ @svg = args.svg
+ @x = args.x ? 1
+ @y = args.y ? 1
+ @width = args.width ? 50
+ @height = args.height ? 34
+ @style = args.style ? STYLE_NORMAL
+ destruct: ->
move: (args) ->
@x = args.x
@y = args.y
proximity: (xy) -> # return the square of the distance to your visible bits
return PROX_MAX + 1
set_style: (style) ->
- return if @style is style
- if style is STYLE_NORMAL
- @el.setAttribute 'style', 'filter: url(#crayon)'
- else
- if @style is STYLE_NORMAL
- @el.setAttribute 'style', ''
- switch style
- when STYLE_NORMAL
- @el.setAttribute 'class', "#{@css_class}"
- when STYLE_SELECTED
- @el.setAttribute 'class', "#{@css_class} selected"
- when STYLE_HOVER
- @el.setAttribute 'class', "#{@css_class} hover"
- when STYLE_DRAGGING
- @el.setAttribute 'class', "#{@css_class} dragging"
- # FIXME when STYLE_EDITING
@style = style
+
+class Control extends Visible
+
+class Widget extends Visible
+ #sub-classes are expected to implement all of these:
+ clone: ->
+ return new Widget @
controls: -> # create controls, return them
return []
hide_controls: ->
@css_class = 'box'
@el = json_to_svg rect:
x: @x + 1
- y: @x + 1
+ y: @y + 1
width: @width - 2
height: @height - 2
- class: 'box'
- style: 'filter: url(#crayon)'
+ class: 'box normal'
+ @svg.appendChild @el
+ destruct: ->
+ super()
+ if @el?
+ @svg.removeChild @el
clone: ->
return new RectWidget @
+ set_style: (style) ->
+ super style
+ set_style_class el: @el, class: 'box', style: style
move: (args) ->
super args
@el.setAttribute 'x', @x + 1
{ feTurbulence: baseFrequency: '.3', numOctaves: '2', type: 'fractalNoise' }
{ feDisplacementMap: scale: '6', xChannelSelector: 'R', in: 'SourceGraphic' }
]
+ svg.appendChild json_to_svg style:
+ type: 'text/css'
+ contents: '.box.normal,.box.hover,.box.selected{filter: url(#crayon)}'
# create canvas border
svg.appendChild json_to_svg rect:
width: width - 2
height: height - 2 - supply_height
class: 'canvas_border'
-
- supply = [
- new RectWidget()
- new RectWidget width: 12, height: 50
- new RectWidget width: 70, height: 12
+
+ supply = {}
+ for args, i in [
+ { width: 40, height: 40 }
+ { width: 12, height: 50 }
+ { width: 70, height: 12 }
]
- for widget, i in supply
- widget.move {
- x: 30 + i * 90 + (70 - widget.width) / 2
- y: (supply_height - widget.height) / 2
+ widget = new RectWidget {
+ width: args.width
+ height: args.height
+ x: 30 + i * 90 + (70 - args.width) / 2
+ y: (supply_height - args.height) / 2
+ svg: svg
}
- svg.appendChild widget.el
-
+ supply[widget.id] = widget
+
# editor state
- on_canvas = []
- selected = []
- editing = [] # has controls
+ on_canvas = {}
+ selected = {}
+ editing = {} # has controls
dragging = false
- dragging_offset = x: 0, y: 0 # from mouse x,y -> widget x,y
+ drag_from = x: 0, y: 0 # mouse was here at last frame of drag
+ shift_key_down = false
- # todo ask widgets
+ deselect_all = (args) ->
+ except = args?.except ? null
+ for id, w of selected
+ w.set_style STYLE_NORMAL
+ delete selected[id]
closest_widget = (widgets, xy) ->
prox = PROX_MAX + 1
closest = null
- for w in widgets
+ for id, w of widgets
new_prox = w.proximity xy
if new_prox < prox
prox = new_prox
closest = w
if prox < PROX_MAX
- return [prox, closest]
+ return closest
return null
svg_event_to_xy = (e) ->
unless svg_offset?
x: Math.round(e.pageX - svg_offset.left)
y: Math.round(e.pageY - svg_offset.top)
}
- $svg.mousedown (e) ->
+ mousedown = (e) ->
+ mousemove e
+ if dragging # two mousedowns in a row?! it happens
+ return mouseup e
xy = svg_event_to_xy e
if xy.y < supply_height
closest = closest_widget supply, xy
if closest?
- closest[1] = closest[1].clone()
- svg.appendChild closest[1].el
+ closest = closest.clone()
+ on_canvas[closest.id] = closest
else
closest = closest_widget on_canvas, xy
if closest?
- selected = [closest[1]]
- closest[1].set_style STYLE_DRAGGING
+ unless (shift_key_down or selected[closest.id]?)
+ deselect_all except: closest
+ selected[closest.id] = closest
+ closest.set_style STYLE_DRAGGING
dragging = true
- dragging_offset.x = closest[1].x - xy.x
- dragging_offset.y = closest[1].y - xy.y
- mousemove e
+ drag_from = xy
+ else
+ deselect_all()
return false
- $svg.mouseup (e) ->
+ mouseup = (e) ->
mousemove e
+ if dragging
+ for id, w of selected
+ if w.y < supply_height
+ w.destruct()
+ delete selected[id]
+ else
+ w.set_style STYLE_SELECTED
dragging = false
- if selected[0]?
- if selected[0].y > supply_height
- on_canvas.push selected[0]
- else
- svg.removeChild selected[0].el
- selected = []
return false
+ prev_hover = null
mousemove = (e) ->
xy = svg_event_to_xy e
if dragging
- xy.x += dragging_offset.x
- xy.y += dragging_offset.y
- selected[0].move xy
+ return if drag_from.x is xy.x and drag_from.y is xy.y
+ rel_x = xy.x - drag_from.x
+ rel_y = xy.y - drag_from.y
+ drag_from = xy
+ for id, w of selected
+ w.move x: w.x + rel_x, y: w.y + rel_y
else
- closest = closest_widget on_canvas, xy
- if closest?
- hover = closest[1]
- else
- hover = null
- for w in on_canvas
- if w is hover
- w.set_style STYLE_HOVER
- else
- w.set_style STYLE_NORMAL
+ hover = closest_widget on_canvas, xy
+ unless hover?
+ hover = closest_widget supply, xy
+ if hover != prev_hover
+ if prev_hover?
+ # FIXME
+ if selected[prev_hover.id]?
+ prev_hover.set_style STYLE_SELECTED
+ else
+ prev_hover.set_style STYLE_NORMAL
+ if hover?
+ hover.set_style STYLE_HOVER
+ prev_hover = hover
return false
+ $svg.mousedown mousedown
+ $svg.mouseup mouseup
$svg.mousemove mousemove
+ $(document).on 'keyup keydown', (e) ->
+ shift_key_down = e.shiftKey
+ return true
#($ document).keydown (e) ->
$ init