JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
drag offset, proper box proximity, hover
[crayon_mockup.git] / auto.coffee
index fcde726..c415d0b 100644 (file)
@@ -2,6 +2,15 @@
 width = 800
 height = 600
 supply_height = 96
+CLICK_FUZ = 10 # this far away from things is close enough to be "clicked on"
+PROX_MAX = CLICK_FUZ * CLICK_FUZ
+
+# constants
+STYLE_NORMAL = 0
+STYLE_SELECTED = 1
+STYLE_HOVER = 2
+STYLE_EDITING = 3
+STYLE_DRAGGING = 4
 
 # json (compiled to javascript and minified) is ~8% smaller than the resulting xml
 json_to_svg = (json) ->
@@ -18,16 +27,44 @@ json_to_svg = (json) ->
 # public vars: x, y, width, height, el
 class Widget
        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 @
        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
+       controls: -> # create controls, return them
+               return []
+       hide_controls: ->
+
 class RectWidget extends Widget
        constructor: (args) ->
                super args
+               @css_class = 'box'
                @el = json_to_svg rect:
                        x: @x + 1
                        y: @x + 1
@@ -35,19 +72,46 @@ class RectWidget extends Widget
                        height: @height - 2
                        class: 'box'
                        style: 'filter: url(#crayon)'
+       clone: ->
+               return new RectWidget @
        move: (args) ->
                super args
                @el.setAttribute 'x', @x + 1
                @el.setAttribute 'y', @y + 1
-       clone: ->
-               return new RectWidget @
+       proximity: (xy) -> # return the square of the distance to your visible bits
+               x = xy.x
+               y = xy.y
+               prox = PROX_MAX + 1
+               in_x = false
+               in_y = false
+               if x > @x - CLICK_FUZ and x < @x + @width + CLICK_FUZ
+                       in_x = true
+                       if y < @y + @height / 2
+                               new_prox = @y - y
+                       else
+                               new_prox = @y + @height - y
+                       new_prox *= new_prox
+                       if new_prox < prox
+                               prox = new_prox
+               if y > @y - CLICK_FUZ and y < @y + @height + CLICK_FUZ
+                       in_y = true
+                       if x < @x + @width / 2
+                               new_prox = @x - x
+                       else
+                               new_prox = @x + @width - x
+                       new_prox *= new_prox
+                       if new_prox < prox
+                               prox = new_prox
+               if in_x and in_y and prox > PROX_MAX
+                       prox = PROX_MAX - 1
+               return prox
+       controls: -> # create controls, return them
+               return []
+       hide_controls: ->
 
 # called automatically on domcontentloaded
 init = ->
-       offset = null
-       selected = []
-       on_canvas = []
-       dragging = false
+       svg_offset = null
        $container = $ '.crayon_mockup'
        svg = json_to_svg svg: width: width, height: height, viewBox: "0 0 #{width} #{height}"
        $svg = $ svg
@@ -80,30 +144,36 @@ init = ->
                }
                svg.appendChild widget.el
        
+       # editor state
+       on_canvas = []
+       selected = []
+       editing = [] # has controls
+       dragging = false
+       dragging_offset = x: 0, y: 0 # from mouse x,y -> widget x,y
+
        # todo ask widgets
        closest_widget = (widgets, xy) ->
-               closest = [10000, null]
+               prox = PROX_MAX + 1
+               closest = null
                for w in widgets
-                       x = w.x + w.width / 2 - xy.x
-                       y = w.y + w.height / 2 - xy.y
-                       dist2 = x * x + y * y
-                       if dist2 < closest[0]
-                               closest = [dist2, w]
-               if closest[1]?
-                       return closest
+                       new_prox = w.proximity xy
+                       if new_prox < prox
+                               prox = new_prox
+                               closest = w
+               if prox < PROX_MAX
+                       return [prox, closest]
                return null
        svg_event_to_xy = (e) ->
-               unless offset?
-                       offset = $svg.offset()
+               unless svg_offset?
+                       svg_offset = $svg.offset()
                return {
-                       x: Math.round(e.pageX - offset.left)
-                       y: Math.round(e.pageY - offset.top)
+                       x: Math.round(e.pageX - svg_offset.left)
+                       y: Math.round(e.pageY - svg_offset.top)
                }
        $svg.mousedown (e) ->
                xy = svg_event_to_xy e
                if xy.y < supply_height
                        closest = closest_widget supply, xy
-                       console.log closest
                        if closest?
                                closest[1] = closest[1].clone()
                                svg.appendChild closest[1].el
@@ -111,7 +181,10 @@ init = ->
                        closest = closest_widget on_canvas, xy
                if closest?
                        selected = [closest[1]]
+                       closest[1].set_style STYLE_DRAGGING
                        dragging = true
+                       dragging_offset.x = closest[1].x - xy.x
+                       dragging_offset.y = closest[1].y - xy.y
                        mousemove e
                return false
        $svg.mouseup (e) ->
@@ -125,11 +198,22 @@ init = ->
                        selected = []
                return false
        mousemove = (e) ->
+               xy = svg_event_to_xy e
                if dragging
-                       xy = svg_event_to_xy e
-                       xy.x -= selected[0].width / 2
-                       xy.y -= selected[0].height / 2
+                       xy.x += dragging_offset.x
+                       xy.y += dragging_offset.y
                        selected[0].move xy
+               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
                return false
        $svg.mousemove mousemove
        #($ document).keydown (e) ->