Browse Source

Adding service to manager, and selection

luis 7 years ago
parent
commit
696527ece8

+ 11 - 1
browser/vue-flow/src/assets/default-theme.css

@@ -6,7 +6,9 @@
   --background-tertiary: rgba(188, 188, 188, 1);
   --normal: #333;
   --normal-secondary: #999;
-  --primary: #aaa;
+
+  /*--primary: #aaa;*/
+  --primary: #57b;
   --node-label: #fff;
   --node-socket: #444;
   --link-hover: #f00;
@@ -129,6 +131,14 @@ input {
   /*filter: url(#highlight-border);*/
 }
 
+.flow-node__selection {
+  transition: all 1s;
+}
+
+.flow-node--selected .flow-node__selection {
+  stroke: var(--primary);
+}
+
 .flow-node--dragging .flow-node__body,
 .flow-view:not(.activity) .flow-node:hover .flow-node__body {
   stroke-width: 1.5;

+ 147 - 32
browser/vue-flow/src/components/flow/manager.vue

@@ -8,10 +8,12 @@
       :class="{
         'flow-linking':linking || stickySockets,
         'activity':dragging || pointerLink.active ,
-        'flow-detail': detailed
+        'flow-detail': detailed,
+        'selecting': (selector)?true:false
       }"
       @dragover.prevent
       @drop="managerDrop"
+      @mousedown="viewPointerDown"
       :width="width"
       :height="height">
       <svg-defs/>
@@ -34,9 +36,10 @@
           v-bind="nodeProps(n)"
           :key="'node' + n.id"
           :id="n.id"
-          @nodePointerDown.prevent="nodeDragStart($event,i)"
+          :selected="nodeSelection[n.id]?true:false"
+          @nodePointerDown.prevent="nodePointerDown($event,i)"
           @socketPointerDown="socketPointerDown(n.id,...arguments)"
-          @nodeDoubleClick="$emit('nodeInspect',n.id)"
+          @nodeDoubleClick="$emit('nodeInspect',n)"
         />
         <!-- mouse link-->
         <flow-link
@@ -44,6 +47,10 @@
           v-if="pointerLink.active"
           v-bind="pointerLink.props"
         />
+        <rect
+          class="flow-selector"
+          :class="{'flow-selector--selecting':(selector)?true:false}"
+          v-bind="selector"/>
 
       </flow-pan-zoom>
     </svg>
@@ -71,7 +78,7 @@ export default {
   name: 'FlowManager',
   components: {FlowNode, FlowLink, FlowPanZoom, SvgDefs},
   props: {
-    'value': {type: Object, default: () => {}},
+    // 'value': {type: Object, default: () => {}},
     'registry': {type: Object, default: () => {}},
     'width': {type: String, default: '800px'},
     'height': {type: String, default: '600px'}
@@ -86,7 +93,9 @@ export default {
       linking: false,
       stickySockets: false,
       detailed: false,
-      pointerLink: {active: false, props: {}, src: {}}
+      pointerLink: {active: false, props: {}, src: {}},
+      selector: null,
+      nodeSelection: {}
     }
   },
   computed: {
@@ -161,6 +170,47 @@ export default {
     }
   },
   mounted () {
+    this.$parent.$on('sparta', () => {
+      console.log('This is weird')
+    })
+    this.$flowService.on('document', (v) => {
+      if (!v.data) { return }
+      const nodes = []
+      for (let n of v.data.nodes) {
+        if (this.dragging && this.dragging.id === n.id) {
+          continue
+        }
+        nodes.push(n)
+      }
+      if (this.dragging) {
+        nodes.push(this.dragging)
+      }
+      this.nodeData = {
+        nodes: nodes,
+        links: v.data.links
+      }
+      this.$nextTick(() => {
+        this.$forceUpdate()
+      })
+
+      this.nodeData = {
+        nodes: v.data.nodes || [],
+        links: v.data.links || []
+      }
+    })
+    this.$flowService.on('nodeUpdate', (v) => {
+      const nodes = v.data
+      const nd = this.nodeData
+      for (let node of nodes) {
+        const idx = nd.nodes.findIndex(n => n.id === node.id)
+        if (idx === -1) { // new node
+          nd.nodes.push(node)
+          continue
+        }
+        this.$set(nd.nodes, idx, node) // new Node
+      }
+    })
+
     this.$nextTick(() => {
       this.$forceUpdate()
     })
@@ -191,6 +241,7 @@ export default {
     // and create some LinkAdd method
     socketPointerDown (nodeId, ev, socket) {
       if (ev.button !== 0) return
+
       const nodeRef = this.$refs.nodes.find(n => n.id === nodeId)
       const node = this.nodeData.nodes.find(n => n.id === nodeId)
 
@@ -264,7 +315,7 @@ export default {
         const output = this.registry[nodeFrom.src].output
         const input = this.registry[nodeTo.src].inputs[link.in]
         // Type checking
-        if (!(output === 'any' || output == input || input === 'any')) {
+        if (!(output === 'any' || output === input || input === 'any')) {
           console.error('LINK: Invalid type')
           return
         }
@@ -285,16 +336,16 @@ export default {
       this.nodeModalTarget = i
       this.nodeModal = true
     },
-    nodeDragStart (ev, i) {
+
+    nodePointerDown (ev, i) {
       document.activeElement && document.activeElement.blur()
-      var tnode = this.nodeData.nodes[i]
+      const tnode = this.nodeData.nodes[i]
       if (ev.button === 1) {
         // remove related links
         this.nodeData.links = this.nodeData.links.filter(l => l.from !== tnode.id && l.to !== tnode.id)
         this.nodeData.nodes.splice(i, 1)
-        this.$emit('input', this.nodeData)
-        this.$emit('nodeRemove', tnode)
-        this.$emit('shouldPersist')
+        this.sendFlowEvent('nodeRemove', tnode)
+        this.sendDocumentUpdate()
         return
       }
       if (ev.button !== 0) return // first button
@@ -302,32 +353,45 @@ export default {
         this.socketPointerDown(tnode.id, ev, {out: 0})
         return
       }
+      // Switch selection
+      if (!this.nodeSelection[tnode.id] && !ev.ctrlKey) this.nodeSelection = {}
+      this.nodeSelection[tnode.id] = tnode
       // we can handle with nodeId and a search
       this.nodeData.nodes.splice(i, 1)
       this.nodeData.nodes.push(tnode) // put in last
 
-      this.dragging = tnode
       // transform CTM
-      const delta = this.transformedPoint(ev.clientX, ev.clientY)
-      delta.x -= tnode.x
-      delta.y -= tnode.y
+      // delta.x -= tnode.x
+      // delta.y -= tnode.y
+
+      const nodeSel = this.nodeData.nodes.filter(n => {
+        return !!this.nodeSelection[n.id]
+      })
 
+      let curP = this.transformedPoint(ev.x, ev.y)
       const drag = (ev) => {
-        const point = this.transformedPoint(ev.clientX, ev.clientY)
-        tnode.x = point.x - delta.x
-        tnode.y = point.y - delta.y
+        this.dragging = tnode
+        const dragP = this.transformedPoint(ev.x, ev.y)
+        for (let n of nodeSel) {
+          n.x += dragP.x - curP.x
+          n.y += dragP.y - curP.y
+        }
+        this.sendFlowEvent('nodeUpdate', nodeSel)
+        curP = dragP
         // Bad possibly
-        this.$emit('input', this.nodeData)
-        this.$emit('nodeUpdate', tnode)
+        // this.$emit('input', this.nodeData)
       }
       const drop = (ev) => {
         document.removeEventListener('mousemove', drag)
         document.removeEventListener('mouseup', drop)
+        if (!this.dragging) {
+          if (!ev.ctrlKey) this.nodeSelection = {}
+          this.nodeSelection[tnode.id] = tnode
+        }
         this.dragging = null
 
-        this.$emit('input', this.nodeData)
-        this.$emit('nodeUpdate', tnode)
-        this.$emit('shouldPersist')
+        this.sendFlowEvent('nodeUpdate', nodeSel)
+        this.sendDocumentUpdate()
       }
       document.addEventListener('mousemove', drag)
       document.addEventListener('mouseup', drop)
@@ -341,24 +405,21 @@ export default {
         src: src
       }
       this.nodeData.nodes.push(newNode)
-      this.$emit('input', this.nodeData)
-      this.$emit('nodeUpdate', newNode)
-      this.$emit('shouldPersist')
+      this.sendFlowEvent('nodeUpdate', [newNode])
+      this.sendDocumentUpdate()
     },
     linkAdd (link) {
       this.nodeData.links.push(link)
-      this.$emit('input', this.nodeData)
-      this.$emit('linkUpdate', link)
-      this.$emit('shouldPersist')
+      this.sendflowEvent('linkUpdate', link)
+      this.sendDocumentUpdate()
     },
     linkRemove (link) {
       const i = this.nodeData.links.findIndex(l => l === link)
       if (i === -1) return
 
       this.nodeData.links.splice(i, 1)
-      this.$emit('input', this.nodeData)
-      this.$emit('linkRemove', link)
-      this.$emit('shouldPersist')
+      this.sendFlowEvent('linkRemove', link)
+      this.sendDocumentUpdate()
     },
     managerDrop (ev) {
       ev.preventDefault()
@@ -371,6 +432,48 @@ export default {
       const pt = this.transformedPoint(ev.x, ev.y)
       this.nodeAdd(reg, pt.x, pt.y)
     },
+    viewPointerDown (ev) {
+      console.log('Starting selector')
+      ev.preventDefault()
+      this.nodeSelection = {}
+      const px = ev.x
+      const py = ev.y
+
+      const p = this.transformedPoint(px, py)
+      this.selector = {x: p.x, y: p.y, width: 0, height: 0}
+      const drag = (ev) => {
+        const p = this.transformedPoint(px, py)
+        const p2 = this.transformedPoint(ev.x, ev.y)
+        const nwidth = p2.x - p.x
+        const nheight = p2.y - p.y
+        this.selector.x = p.x
+        this.selector.y = p.y
+        this.selector.width = nwidth
+        this.selector.height = nheight
+        if (nwidth < 0) {
+          this.selector.x = p2.x
+          this.selector.width = -nwidth
+        }
+        if (nheight < 0) {
+          this.selector.y = p2.y
+          this.selector.height = -nheight
+        }
+      }
+      const drop = (ev) => {
+        this.selector = null
+        document.removeEventListener('mousemove', drag)
+        document.removeEventListener('mouseup', drop)
+      }
+      document.addEventListener('mousemove', drag)
+      document.addEventListener('mouseup', drop)
+    },
+    sendFlowEvent (type, param) {
+      this.$flowService[type](param)
+    },
+
+    sendDocumentUpdate (nodeData) {
+      this.$flowService.documentUpdate(this.nodeData, this.$route.params.sessId)
+    },
     // HELPERS depending on svg ref
     createSVGPoint (x, y) {
       const p = this.$refs.svg.createSVGPoint()
@@ -434,4 +537,16 @@ function guid () {
   fill:transparent;
 }
 
+.flow-selector {
+  pointer-events:none;
+  opacity:0;
+  /* TODO: theme */
+  stroke: rgba(30,100,255,1);
+  fill: rgba(30,100,255,0.3);
+}
+
+.flow-selector.flow-selector--selecting {
+  opacity:1;
+}
+
 </style>

+ 27 - 7
browser/vue-flow/src/components/flow/node.vue

@@ -1,11 +1,21 @@
 <template>
   <g
     class="flow-node"
-    :class="{'flow-node--dragging':dragging}"
-    @mousedown="$emit('nodePointerDown',$event)"
+    :class="{
+      'flow-node--dragging':dragging,
+      'flow-node--selected': selected
+    }"
+    @mousedown.stop.prevent="$emit('nodePointerDown',$event)"
     @dblclick="$emit('nodeDoubleClick',$event)"
   >
-
+    <rect
+      class="flow-node__selection"
+      stroke-dasharray="7,3"
+      :x="bodyProps.x-4"
+      :y="bodyProps.y-4"
+      :width="bodyProps.width+8"
+      :height="bodyProps.height+8"
+    />
     <rect
       ref="body"
       class="flow-node__body"
@@ -16,8 +26,7 @@
       ref="label"
       class="flow-node__label"
       v-bind="labelProps"
-      text-anchor="middle"
-    >
+      text-anchor="middle">
       <tspan x="0" dy="1em" v-for="s in labelWrap" >
         {{ s }}
       </tspan>
@@ -42,7 +51,7 @@
       :data-nodeid="id"
       :data-out="0"
       :key="'out'+0"
-      :class="{ 'flow-node__socket--match': match.out && (output == match.out || match.out == 'any' || output == 'any')}"
+      :class="{ 'flow-node__socket--match': match.out && (output == match.out || match.out == 'any' || output == 'any'), }"
       v-bind="outputProps(0)"
       @mousedown.stop.prevent="socketPointerDown($event, {out:0})"
     />
@@ -75,6 +84,7 @@ export default {
     'output': {type: String, default: ''},
     'match': {type: Object, default: () => {}},
     'dragging': {type: Boolean, default: false},
+    'selected': {type: Boolean, default: false},
 
     'nodeStyle': {type: Object, default: () => {}}
     /* 'type': {type: String, default: ''},
@@ -113,7 +123,7 @@ export default {
     labelProps () {
       return {
         x: 0,
-        y: -0.1 + 'em',
+        y: 0,
         fill: this.textColor,
         // transform: `translate(${-this.labelRect.width / 2},${-this.labelRect.height / 2})`
         transform: `translate(0,${-this.labelRect.height / 2})`
@@ -250,4 +260,14 @@ for hidden
   opacity:1;
 }
 
+.flow-node .flow-node__selection {
+  opacity:0;
+  stroke-width:1;
+  pointer-events:none;
+  transition: .3s;
+}
+.flow-node--selected .flow-node__selection {
+  opacity:1;
+}
+
 </style>

+ 3 - 2
browser/vue-flow/src/components/flow/panzoom.vue

@@ -11,7 +11,7 @@
       class="flow-pan-zoom__transformer"
       width="100%"
       height="100%"
-      @mousedown.stop.prevent="dragStart"/>
+      @mousedown="dragStart"/>
     <g
       ref="transform"
       v-bind="transformProps">
@@ -68,8 +68,9 @@ export default {
     // panStart
     dragStart (ev) {
       document.activeElement && document.activeElement.blur()
-      if (ev.button !== 0) return // first button
+      if (ev.button !== 1) return // first button
       if (ev.target !== this.$refs.transformer) return
+      ev.stopPropagation()
 
       const drag = (ev) => {
         this.moving = true

+ 5 - 40
browser/vue-flow/src/components/main.vue

@@ -56,19 +56,9 @@
 
           <flow-manager
             :registry="registry"
-            v-model="nodeData"
             @funcsPanelToggle="funcsActive=!funcsActive"
-
-            @linkUpdate="sendFlowEvent('linkUpdate',$event)"
-            @linkRemove="sendFlowEvent('linkRemove',$event)"
-
-            @nodeUpdate="sendFlowEvent('nodeUpdate',$event)"
-            @nodeRemove="sendFlowEvent('nodeRemove',$event)"
-
             @nodeInspect="nodeInspect(...arguments)"
 
-            @shouldPersist="sendDocumentUpdate"
-
             width="100%"
             height="100%"/>
         </hx-split>
@@ -152,8 +142,6 @@ export default {
         'lineGraph': { group: 'Visualization', inputs: ['[]float32', '[]float32'], style: {'color': '#9a9'} }
       },
 
-      nodeData: {nodes: [], links: []},
-
       nodeInspectTarget: false,
 
       funcsSize: '250px',
@@ -165,7 +153,7 @@ export default {
     }
   },
   watch: {
-    nodeInspectTarget: {
+    /* nodeInspectTarget: {
       handler (val, oldVal) {
         if (!val === null && !oldVal) { return }
         if (!val) {
@@ -175,7 +163,7 @@ export default {
         this.sendFlowEvent('nodeUpdate', this.nodeInspectTarget)
       },
       deep: true
-    }
+    } */
   },
 
   mounted () {
@@ -185,23 +173,6 @@ export default {
         this.$router.push('/' + v.id) // Swap to ID
       }
     })
-    this.$flowService.on('document', (v) => {
-      if (!v.data) return
-      this.nodeData = {
-        nodes: v.data.nodes || [],
-        links: v.data.links || []
-      }
-    })
-    this.$flowService.on('nodeUpdate', (v) => {
-      const node = v.data
-      const nd = this.nodeData
-      const idx = nd.nodes.findIndex(n => n.id === node.id)
-      if (idx === -1) { // new node
-        nd.nodes.push(node)
-        return
-      }
-      this.$set(nd.nodes, idx, node) // new Node
-    })
     // Connected
     this.$flowService.connected(() => {
       // Make this in a service
@@ -213,8 +184,8 @@ export default {
     })
   },
   methods: {
-    nodeInspect (id) {
-      this.nodeInspectTarget = this.nodeData.nodes.find(n => n.id === id)
+    nodeInspect (node) { // node
+      this.nodeInspectTarget = node
       this.$nextTick(() => {
         if (!this.$refs.modalInput) return
         this.$refs.modalInput.setSelectionRange(0, this.$refs.modalInput.value.length)
@@ -223,14 +194,8 @@ export default {
     },
     funcsSizeUpdate (ev, size) {
       this.funcsSize = size
-    },
-    sendFlowEvent (type, param) {
-      this.$flowService[type](param)
-    },
-    // Update individual nodes/links
-    sendDocumentUpdate (nodeData) {
-      this.$flowService.documentUpdate(this.nodeData, this.$route.params.sessId)
     }
+    // Update individual nodes/links
   }
 }
 

+ 1 - 1
browser/vue-flow/src/components/nodeinspect.vue

@@ -2,7 +2,7 @@
 <script>
 export default {
   props: {
-    nodeModal: { type: Boolean, default: False}
+    nodeModal: { type: Boolean, default: false}
   }
 }
 </script>