Changeset - 92104dcd33e2
[Not reviewed]
default
0 4 0
Drew Perttula - 7 years ago 2018-05-19 23:13:55
drewp@bigasterisk.com
finish mouse event routing. inlineAttrs display again.
Ignore-this: 5e5438f724a868ac12905a77e054d55
4 files changed with 77 insertions and 102 deletions:
0 comments (0 inline, 0 general)
light9/web/timeline/inline-attrs.coffee
Show inline comments
 
log = console.log
 

	
 
coffeeElementSetup(class InlineAttrs extends Polymer.Element
 
  @is: "light9-timeline-note-inline-attrs"
 
  @getter_properties:
 
    graph: { type: Object, notify: true }
 
    song: { type: String, notify: true }
 
    uri: { type: String, notify: true }  # the Note
 
    rect: { type: Object, notify: true }
 
    effect: { type: String, notify: true }
 
    config: { type: Object } # just for setup
 
    uri: { type: Object, notify: true }  # the Note
 
    effect: { type: Object, notify: true }
 
    colorScale: { type: String, notify: true }
 
    noteLabel: { type: String, notify: true }
 
    selection: { type: Object, notify: true }
 
  @getter_observers: [
 
    'addHandler(graph, uri)'
 
    'onColorScale(graph, uri, colorScale)'
 
    '_onConfig(config)'
 
    ]
 
  _onConfig: ->
 
    @uri = @config.uri
 
    for side in ['top', 'left', 'width', 'height']
 
      @.style[side] = @config[side] + 'px'
 

	
 
  displayed: ->
 
    @querySelector('light9-color-picker').displayed()
 
    
 
  onColorScale: ->
 
    return
 
    U = (x) => @graph.Uri(x)
 
    if @colorScale == @colorScaleFromGraph
 
      return
 
    @editAttr(@song, @uri, U(':colorScale'), @graph.Literal(@colorScale))
 

	
 
  editAttr: (song, note, attr, value) ->
 
    U = (x) => @graph.Uri(x)
 
    if not song?
 
      log("can't edit inline attr yet, no song")
 
      return
 
    quad = (s, p, o) => {subject: s, predicate: p, object: o, graph: song}
 

	
 
@@ -39,24 +47,25 @@ coffeeElementSetup(class InlineAttrs ext
 
    if existingColorScaleSetting
 
      @graph.patchObject(existingColorScaleSetting, U(':value'), value, song)
 
    else
 
      setting = @graph.nextNumberedResource(note + 'set')
 
      patch = {delQuads: [], addQuads: [
 
        quad(note, U(':setting'), setting)
 
        quad(setting, U(':effectAttr'), attr)
 
        quad(setting, U(':value'), value)
 
        ]}
 
      @graph.applyAndSendPatch(patch)
 
    
 
  addHandler: ->
 
    return
 
    @graph.runHandler(@update.bind(@), "update inline attrs #{@uri}")
 
    
 
  update: ->
 
    #console.time('attrs update')
 
    U = (x) => @graph.Uri(x)
 
    @effect = @graph.uriValue(@uri, U(':effectClass'))
 
    @noteLabel = @uri.replace(/.*\//, '')
 

	
 
    existingColorScaleSetting = null
 
    for setting in @graph.objects(@uri, U(':setting'))
 
      ea = @graph.uriValue(setting, U(':effectAttr'))
 
      value = @graph.stringValue(setting, U(':value'))
light9/web/timeline/inline-attrs.html
Show inline comments
 
<link rel="import" href="/lib/polymer/polymer-element.html">
 
<link rel="import" href="../light9-color-picker.html">
 
<link rel="import" href="../edit-choice.html">
 

	
 
<!-- sometimes we draw attrs within the shape of a note. -->
 
<dom-module id="light9-timeline-note-inline-attrs">
 
  <template>
 
    <style>
 
     #top {
 
     :host {
 
         position: absolute;
 

	
 
         display: block;
 
         overflow: hidden;
 
         background: rgba(19, 19, 19, 0.65);
 
         border-radius: 6px;
 
         border: 1px solid #313131;
 
         padding: 3px;
 
         z-index: 2;
 
         color: white;
 
     }
 
    </style>
 
    <div id="top" style$="left: [[rect.left]]px; top: [[rect.top]]px; width: [[rect.width]]px; height: [[rect.height]]px; display: [[rect.display]]">
 

	
 
      <div>note [[noteLabel]] <button on-click="onDel">del</button></div>
 
      <table>
 
        <tr><th>effect:</th><td><edit-choice graph="{{graph}}" uri="{{effect}}"></edit-choice></td></tr>
 
        <tr><th>colorScale:</th><td>
 
          <light9-color-picker color="{{colorScale}}"></light9-color-picker>
 
        </td></tr>
 
      </table>
 
    </div>
 

	
 
  </template>
 
  <script src="inline-attrs.js"></script>
 
</dom-module>
light9/web/timeline/timeline-elements.html
Show inline comments
 
@@ -110,78 +110,30 @@
 
     }
 
    </style>
 
    <div id="top">
 
      <light9-timeline-time-axis id="time" view-state="{{viewState}}"></light9-timeline-time-axis>
 
      <light9-timeline-audio id="audio"
 
                             graph="{{graph}}"
 
                             song="{{song}}"
 
                             show="{{show}}"
 
                             zoom="{{zoomFlattened}}">
 
      </light9-timeline-audio>
 
    </div>
 
    <div id="rows"></div>
 
  </template>
 
</dom-module>
 

	
 

	
 
<!--
 
     SVG or canvas that draws these:
 
       - background grids
 
       - zoom arcs
 
       - notes
 
     
 
     This element is not responsible for any hit detection. Things you click (rows,
 
     notes, adjusters, etc) are caught on their respective elements. (But is that
 
     wrong in the case of notes?)
 
   -->
 
<dom-module id="light9-timeline-diagram-layer">
 
  <template>
 
    <style>
 
     :host {
 
         pointer-events: none;
 
     }
 
     svg {
 
         pointer-events: none;
 
         width: 100%;
 
         height: 100%;
 
     }
 
     #notes > path {
 
         stroke:#000000;
 
         stroke-width:1.5;
 
     }
 
     #notes > path.hover {
 
         stroke-width: 1.5;
 
         stroke: #888;
 
     }
 
     #notes > path.selected {
 
         stroke-width: 5;
 
         stroke: red;
 
     }
 
    </style>
 
    <svg xmlns="http://www.w3.org/2000/svg"
 
         xmlns:svg="http://www.w3.org/2000/svg"
 
         xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" >
 
      <g id="layer1">
 
        <text
 
            xml:space="preserve"
 
            style="font-size:13px;line-height:125%;font-family:'Verana Sans';-inkscape-font-specification:'Verana Sans';text-align:start;text-anchor:start;fill:#000000;"
 
            x="-338.38403"
 
            y="631.3988"
 
            id="text4290"
 
            sodipodi:linespacing="125%" ><tspan sodipodi:role="line" id="tspan4292" x="-338.38403" y="631.3988">spotchase</tspan></text>
 
        <g id="zzztimeAxis" transform="translate(0,40)"></g>
 
        <g id="mouse"></g>
 
        <g id="notes"></g>
 
      </g>
 
    </svg>
 
    <template is="dom-repeat" items="{{inlineAttrConfigs}}">
 
      <light9-timeline-note-inline-attrs graph="{{graph}}"
 
                                         song="{{song}}"
 
                                         config="{{item}}"
 
      ></light9-timeline-note-inline-attrs>
 
    </template>
 
  </template>
 
</dom-module>
 

	
 
<dom-module id="light9-cursor-canvas">
 
  <template>
 
    <style>
 
     #canvas, :host {
 
         pointer-events: none;
 
     }
 
    </style>
 
    <canvas id="canvas"></canvas>
 
  </template>
light9/web/timeline/timeline.coffee
Show inline comments
 
@@ -283,35 +283,36 @@ coffeeElementSetup(class TimelineEditor 
 

	
 
# plan: in here, turn all the notes into simple js objects with all
 
# their timing data and whatever's needed for adjusters. From that, do
 
# the brick layout. update only changing adjusters.
 
coffeeElementSetup(class TimeZoomed extends Polymer.mixinBehaviors([Polymer.IronResizableBehavior], Polymer.Element)
 
  @is: 'light9-timeline-time-zoomed'
 
  @getter_properties:
 
    graph: { type: Object, notify: true }
 
    project: { type: Object }
 
    selection: { type: Object, notify: true }
 
    song: { type: String, notify: true }
 
    viewState: { type: Object, notify: true }
 
    inlineAttrConfigs: { type: Array, value: [] } # only for inlineattrs that should be displayed
 
  @getter_observers: [
 
    '_onGraph(graph, setAdjuster, song, viewState, project)',
 
  ]
 
  constructor: ->
 
    super()
 
    @notes = []
 
    @stage = new PIXI.Container()
 
    @stage.interactive=true
 
    
 
    @renderer = PIXI.autoDetectRenderer({
 
         backgroundColor: 0x606060,
 
        backgroundColor: 0x606060,
 
        antialias: true,
 
        forceCanvas: true,
 
    })
 
     
 
  ready: ->
 
    super.ready()
 

	
 
    @addEventListener('iron-resize', @_onResize.bind(@))
 
    Polymer.RenderStatus.afterNextRender(this, @_onResize.bind(@))
 
    
 
    @$.rows.appendChild(@renderer.view)
 

	
 
@@ -343,25 +344,25 @@ coffeeElementSetup(class TimeZoomed exte
 
    @stage.removeChildren()
 
    n.destroy() for n in @notes
 
    @notes = []
 
    
 
    noteNum = 0
 
    for uri in _.sortBy(songNotes, 'id')
 
      con = new PIXI.Container()
 
      con.interactive=true
 
      @stage.addChild(con)
 
      
 
      row = noteNum % 6
 
      rowTop = @viewState.rowsY() + 20 + 150 * row
 
      note = new Note(con, @project, @graph, @selection, uri, @setAdjuster, U(@song), @viewState, rowTop, rowTop + 140)
 
      note = new Note(@, con, @project, @graph, @selection, uri, @setAdjuster, U(@song), @viewState, rowTop, rowTop + 140)
 
      @notes.push(note)
 
      noteNum = noteNum + 1
 
 
 
    @renderer.render(@stage)
 
    
 
  onDrop: (effect, pos) ->
 
    U = (x) => @graph.Uri(x)
 

	
 
    return unless effect and effect.match(/^http/)
 

	
 
    # we could probably accept some initial overrides right on the
 
    # effect uri, maybe as query params
 
@@ -370,44 +371,61 @@ coffeeElementSetup(class TimeZoomed exte
 
      if @graph.contains(effect, RDF + 'type', U(':LightSample'))
 
        effect = @project.makeEffect(effect)
 
      else
 
        log("drop #{effect} is not an effect")
 
        return
 

	
 
    dropTime = @viewState.zoomInX.invert(pos.e(1))
 

	
 
    desiredWidthX = @offsetWidth * .3
 
    desiredWidthT = @viewState.zoomInX.invert(desiredWidthX) - @viewState.zoomInX.invert(0)
 
    desiredWidthT = Math.min(desiredWidthT, @zoom.duration() - dropTime)
 
    @project.makeNewNote(effect, dropTime, desiredWidthT)
 

	
 
  updateInlineAttrs: (note, config) ->
 
    if not config?
 
      index = 0
 
      for c in @inlineAttrConfigs
 
        if c.uri.equals(note)
 
          @splice('inlineAttrConfigs', index)
 
          return
 
        index += 1
 
    else
 
      index = 0
 
      for c in @inlineAttrConfigs
 
        if c.uri.equals(note)
 
          @splice('inlineAttrConfigs', index, 1, config)
 
          return
 
        index += 1
 
      @push('inlineAttrConfigs', config)
 
)
 

	
 

	
 
coffeeElementSetup(class TimeAxis extends Polymer.Element
 
  @is: "light9-timeline-time-axis",
 
  @getter_properties:
 
    viewState: { type: Object, notify: true, observer: "onViewState" }
 
  onViewState: ->
 
    ko.computed =>
 
      dependOn = [@viewState.zoomSpec.t1(), @viewState.zoomSpec.t2()]
 
      pxPerTick = 50
 
      axis = d3.axisTop(@viewState.zoomInX).ticks(@viewState.width() / pxPerTick)
 
      d3.select(@$.axis).call(axis)
 
)
 

	
 

	
 
# Maintains a pixi object and some adjusters corresponding to a note
 
# Maintains a pixi object, some adjusters, and inlineattrs corresponding to a note
 
# in the graph.
 
class Note
 
  constructor: (@container, @project, @graph, @selection, @uri, @setAdjuster, @song, @viewState, @rowTopY, @rowBotY) ->
 
  constructor: (@parentElem, @container, @project, @graph, @selection, @uri, @setAdjuster, @song, @viewState, @rowTopY, @rowBotY) ->
 
    @adjusterIds = {} # id : true
 
    @graph.runHandler(@draw.bind(@), 'note draw')
 

	
 
  destroy: ->
 
    log('destroy', @uri.value)
 
    @isDetached = true
 
    @clearAdjusters()
 

	
 
  clearAdjusters: ->
 
    for i in Object.keys(@adjusterIds)
 
      @setAdjuster(i, null)
 

	
 
@@ -430,38 +448,44 @@ class Note
 
    screenPts = (new PIXI.Point(@viewState.zoomInX(pt.e(1)), yForV(pt.e(2))) for pt in worldPts)
 

	
 
    @container.removeChildren()    
 
    graphics = new PIXI.Graphics({nativeLines: false})
 
    graphics.interactive = true
 
    @container.addChild(graphics)
 

	
 
    shape = new PIXI.Polygon(screenPts)
 
    graphics.beginFill(@_noteColor(effect), .313)
 
    graphics.drawShape(shape)
 
    graphics.endFill()
 

	
 
    # stroke should vary with @selection.hover() == @uri and with @uri in @selection.selected()
 
    # 
 
    # #notes > path.hover {stroke-width: 1.5; stroke: #888;}
 
    # #notes > path.selected {stroke-width: 5; stroke: red;}
 
    graphics.lineStyle(2, 0xffd900, 1)
 
    graphics.moveTo(screenPts[0].x, screenPts[0].y)
 
    for p in screenPts.slice(1)
 
      graphics.lineTo(p.x, p.y)
 

	
 
    graphics.on 'mousedown', =>
 
    graphics.on 'mousedown', (ev) =>
 
      log('down gfx', @uri.value)
 
      @_onMouseDown(ev)
 

	
 
    graphics.on 'mouseover', =>
 
      log('hover', @uri.value)
 
      @selection.hover(@uri)
 

	
 
    graphics.on 'mouseout', =>
 
      log('hoverout', @uri.value)
 
      
 
      @selection.hover(null)
 

	
 
    @graphics = graphics
 
    curveWidthCalc = () => @project.curveWidth(worldPts)
 
    @_updateAdjusters(screenPts, worldPts, curveWidthCalc, yForV, @song)
 
    @_updateInlineAttrs(screenPts)
 
    
 
  onUri: ->
 
    @graph.runHandler(@update.bind(@), "note updates #{@uri}")
 

	
 
  patchCouldAffectMe: (patch) ->
 
    if patch and patch.addQuads # sometimes patch is a polymer-sent value. @update is used as a listener too
 
      if patch.addQuads.length == patch.delQuads.length == 1
 
@@ -486,41 +510,44 @@ class Note
 

	
 
    @_updateDisplay()
 

	
 
  _updateAdjusters: (screenPts, worldPts, curveWidthCalc, yForV, ctx) ->
 
    if screenPts[screenPts.length - 1].x - screenPts[0].x < 100
 
      @clearAdjusters()
 
    else
 
      @_makeOffsetAdjuster(yForV, curveWidthCalc, ctx)
 
      @_makeCurvePointAdjusters(yForV, worldPts, ctx)
 
      @_makeFadeAdjusters(yForV, ctx, worldPts)
 

	
 
  _updateInlineAttrs: (screenPts) ->
 
    w = 280
 
    
 
    leftX = Math.max(2, screenPts[Math.min(1, screenPts.length - 1)].x + 5)
 
    rightX = screenPts[Math.min(2, screenPts.length - 1)].x - 5
 
    if screenPts.length < 3
 
      rightX = leftX + 120
 
    w = 250
 
    h = 110
 
    wasHidden = @inlineRect?.display == 'none'
 
    @inlineRect = {
 
      rightX = leftX + w
 

	
 
    if rightX - leftX < w or rightX < w or leftX > @parentElem.offsetWidth
 
      @parentElem.updateInlineAttrs(@uri, null)
 
      return
 

	
 
    config = {
 
      uri: @uri,
 
      left: leftX,
 
      top: @offsetTop + @offsetHeight - h - 5,
 
      top: @rowTopY + 5,
 
      width: w,
 
      height: h,
 
      display: if rightX - leftX > w then 'block' else 'none'
 
      height: @rowBotY - @rowTopY - 15,
 
      }
 
    if wasHidden and @inlineRect.display != 'none'
 
      @async =>
 
        @querySelector('light9-timeline-note-inline-attrs')?.displayed()
 

	
 
    @parentElem.updateInlineAttrs(@uri, config)
 
    
 
  _makeCurvePointAdjusters: (yForV, worldPts, ctx) ->
 
    for pointNum in [0...worldPts.length]
 
      @_makePointAdjuster(yForV, worldPts, pointNum, ctx)
 

	
 
  _makePointAdjuster: (yForV, worldPts, pointNum, ctx) ->
 
    U = (x) => @graph.Uri(x)
 

	
 
    adjId = @uri.value + '/p' + pointNum
 
    @adjusterIds[adjId] = true
 
    @setAdjuster adjId, =>
 
      adj = new AdjustableFloatObject({
 
@@ -570,59 +597,43 @@ class Note
 
    @_makeFadeAdjuster(yForV, ctx, @uri.value + '/fadeOut', n - 2, n - 1, $V([50, -10]))
 

	
 
  _makeFadeAdjuster: (yForV, ctx, adjId, i0, i1, offset) ->
 
    return # not ready- AdjustableFade looks in Note object
 
    @adjusterIds[adjId] = true
 
    @setAdjuster adjId, => new AdjustableFade(yForV, i0, i1, @, offset, ctx)
 
    
 
  _suggestedOffset: (pt) ->
 
    if pt.e(2) > .5
 
      $V([0, 30])
 
    else
 
      $V([0, -30])
 
    
 
  
 
  
 
  _addNoteListeners: (elem, uri) ->
 
    elem.addEventListener 'mouseenter', =>
 
      @selection.hover(uri)
 
    elem.addEventListener 'mousedown', (ev) =>
 
      sel = @selection.selected()
 
      if ev.getModifierState('Control')
 
        if uri in sel
 
          sel = _.without(sel, uri)
 
        else
 
          sel.push(uri)
 
  _onMouseDown: (ev) ->
 
    sel = @selection.selected()
 
    if ev.data.originalEvent.ctrlKey
 
      if @uri in sel
 
        sel = _.without(sel, @uri)
 
      else
 
        sel = [uri]
 
      @selection.selected(sel)
 
    elem.addEventListener 'mouseleave', =>
 
      @selection.hover(null)
 
        sel.push(@uri)
 
    else
 
      sel = [@uri]
 
    @selection.selected(sel)
 

	
 
  _noteColor: (effect) ->
 
    effect = effect.value
 
    if effect in ['http://light9.bigasterisk.com/effect/blacklight',
 
      'http://light9.bigasterisk.com/effect/strobewarm']
 
      hue = 0
 
      sat = 100
 
    else        
 
      hash = 0
 
      for i in [(effect.length-10)...effect.length]
 
        hash += effect.charCodeAt(i)
 
      hue = (hash * 8) % 360
 
      sat = 40 + (hash % 20) # don't conceal colorscale too much
 

	
 
    return parseInt(tinycolor.fromRatio({h: hue / 360, s: sat / 100, l: .58}).toHex(), 16)
 

	
 
  _noteInDiagram: (uri) ->
 
    return !!@elemById[uri.value + '/area']
 

	
 
  _updateNotePathClasses: (uri, elem) ->
 
    ko.computed =>
 
      return if not @_noteInDiagram(uri)
 
      classes = 'light9-timeline-diagram-layer ' + (if @selection.hover() == uri then 'hover' else '') + ' '  + (if uri in @selection.selected() then 'selected' else '')
 
      elem.setAttribute('class', classes)
 
    
 
    #elem = @getOrCreateElem(uri+'/label', 'noteLabels', 'text', {style: "font-size:13px;line-height:125%;font-family:'Verana Sans';text-align:start;text-anchor:start;fill:#000000;"})
 
    #elem.setAttribute('x', curvePts[0].e(1)+20)
 
    #elem.setAttribute('y', curvePts[0].e(2)-10)
 
    #elem.innerHTML = effectLabel;
0 comments (0 inline, 0 general)