From b5d0e345a983b22261043d342e201a989abd90fd Mon Sep 17 00:00:00 2001
From: EdwardAJ <13517115@std.stei.itb.ac.id>
Date: Thu, 9 Apr 2020 07:11:28 +0700
Subject: [PATCH] Finish toolbar

---
 frontend/components/label/Box.vue    |  57 +++++++---
 frontend/pages/viewer/index-edit.vue | 152 +++++++++++++++++----------
 frontend/pages/viewer/index.vue      |  43 +++++---
 3 files changed, 166 insertions(+), 86 deletions(-)

diff --git a/frontend/components/label/Box.vue b/frontend/components/label/Box.vue
index 350b319..808ac75 100644
--- a/frontend/components/label/Box.vue
+++ b/frontend/components/label/Box.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="box-wrapper">
+  <div v-if="!isDeleted" class="box-wrapper">
     <!-- Identifier Square -->
     <div
       v-if="bIndex !== -1"
@@ -10,9 +10,17 @@
       id: {{ bIndex }}
     </div>
 
+    <div v-if="isDeleteActive">
+      <i
+        class="icon-trash fas fa-trash shadow"
+        :style="{ top: bTop -8 + 'px', left: bLeft + -4 + 'px'}"
+        @click="deleteBox"
+      />
+    </div>
+
     <!-- Top-Left Circle -->
     <div
-      v-if="bActive"
+      v-if="bActive && bAction === 'resize-box'"
       class="circle"
       :style="{ top: bTop + -6 + 'px', left: bLeft + -6 + 'px'}"
       @mousedown="isLeftCircleActive = true"
@@ -22,7 +30,7 @@
 
     <!-- Bottom-Right Circle -->
     <div
-      v-if="bActive"
+      v-if="bActive && bAction === 'resize-box'"
       class="circle"
       :style="{ top: bTop + bHeight + -8 + 'px', left: bLeft + bWidth + -8 + 'px'}"
       @mousedown="isRightCircleActive = true"
@@ -42,9 +50,8 @@
     />
 
     <div
-      class="box"
       :style="{top: bTop + 'px', left: bLeft + 'px', width: bWidth + 'px', height: bHeight + 'px'}"
-      :class="{'active': bActive}"
+      :class="{'active': bActive, 'box': !isDeleteActive, 'box-delete': isDeleteActive}"
       @mousedown="selectBox"
     />
   </div>
@@ -89,6 +96,10 @@ export default {
       type: String,
       default: ''
     },
+    bAction: {
+      type: String,
+      default: 'add-box'
+    },
     suggestType: {
       type: String,
       default: 'label'
@@ -103,7 +114,8 @@ export default {
       isLeftCircleActive: false,
       isRightCircleActive: false,
       isOnResize: false,
-      isSuggestActive: false
+      isSuggestActive: false,
+      isDeleted: false
     }
   },
   computed: {
@@ -113,22 +125,18 @@ export default {
       } else {
         return 80
       }
+    },
+    isDeleteActive () {
+      return this.bAction === 'delete-box'
     }
   },
-  mounted () {
-    window.addEventListener('keyup', (event) => {
-      // Delete key
-      if (this.canDelete && !this.isSuggestActive && (event.keyCode === 8 || event.keyCode === 46)) {
-        this.deleteBox()
-      }
-    })
-  },
   methods: {
     selectBox () {
       this.$emit("onSelect", this.bIndex)
     },
     deleteBox () {
-      if (this.bActive) {
+      if (this.isDeleteActive) {
+        this.isDeleted = true
         this.$emit("onDelete", this.bIndex)
       }
     },
@@ -178,17 +186,21 @@ export default {
 </script>
 
 <style scoped>
-    .box {
+    .box, .box-delete {
         position: absolute;
         border: 3px #F0F801 solid;
         z-index: 3;
     }
 
-    .box:hover, .box.active {
+    .box:hover, .box-delete:hover , .box.active {
       background-color: rgba(144, 238, 144, .3);
       cursor: pointer;
     }
 
+    .box-delete:hover {
+      background-color: rgba(214, 36, 70, .3);
+    }
+
     #bIndex {
       position: absolute;
       z-index: 6;
@@ -233,5 +245,16 @@ export default {
       z-index: 20;
     }
 
+    .icon-trash {
+      color: red;
+      position: absolute;
+      z-index: 20;
+      cursor: pointer;
+    }
+
+    .icon-trash:hover {
+      color: #F0F801;
+    }
+
     
 </style>
\ No newline at end of file
diff --git a/frontend/pages/viewer/index-edit.vue b/frontend/pages/viewer/index-edit.vue
index d15104c..86c3a58 100644
--- a/frontend/pages/viewer/index-edit.vue
+++ b/frontend/pages/viewer/index-edit.vue
@@ -1,58 +1,70 @@
 <template>
   <div ref="imageWrapper" class="viewer-background">
-    <div class="btn-close-section">
-      <button
-        type="button"
-        class="btn-label-no-border btn-sm btn-light btn-close-text mt-2"
-        aria-label="Close"
-        @click="closeViewer()"
-      > 
-        <span aria-hidden="true">&times;</span>
-      </button>
-    </div>
-    <div class="viewer-wrapper center center-horizontal" @mousedown="stopDrawingBox">
-      <div id="image" ref="img">
-        <img
-          v-if="dataReady"
-          ref="image"
-          draggable="false"
-          class="image"
-          :src="image.url"
-          @mousedown="onMouseDownHandler"
-          @mousemove="changeBox"
-          @mouseup="stopDrawingBox"
-        >
-        <Box
-          v-if="drawingBox.active"
-          :b-width="drawingBox.width"
-          :b-height="drawingBox.height"
-          :b-top="drawingBox.top"
-          :b-left="drawingBox.left"
-        />
-        <div v-for="i in Object.keys(boxes)" :key="i">
+    <div class="flex-viewer">
+      <Toolbar
+        @onIconClick="setBoxAction($event)"
+      />
+      <div class="viewer-wrapper center center-horizontal" @mousedown="stopDrawingBox">
+        <div id="image">
+          <img
+            v-if="dataReady"
+            ref="image"
+            draggable="false"
+            class="image"
+            :style="{ cursor: cssCursor}"
+            :src="image.url"
+            @mousedown="onMouseDownHandler"
+            @mousemove="changeBox"
+            @mouseup="stopDrawingBox"
+          >
           <Box
-            v-if="boxes[i]"
-            :key="i"
-            :suggest-type="'edit'"
-            :b-width="boxes[i].width"
-            :b-height="boxes[i].height"
-            :b-top="boxes[i].top"
-            :b-left="boxes[i].left"
-            :b-active="i === activeBoxIndex"
-            :b-index="parseInt(i)"
-            :b-content="boxes[i].content"
-            :can-delete="canDelete"
-            @onStopResize="changeBoxAttribute($event, i)"
-            @onDelete="deleteBox(i)"
-            @onSelect="makeCurrentBoxActive(i)"
-            @onDisableForm="changeBoxContent($event, i)"
-            @onEnableForm="makeCurrentBoxActive(i)"
+            v-if="drawingBox.active"
+            :b-width="drawingBox.width"
+            :b-height="drawingBox.height"
+            :b-top="drawingBox.top"
+            :b-left="drawingBox.left"
           />
+          <div v-for="i in Object.keys(boxes)" :key="i">
+            <Box
+              v-if="boxes[i]"
+              :b-width="boxes[i].width"
+              :b-height="boxes[i].height"
+              :b-top="boxes[i].top"
+              :b-left="boxes[i].left"
+              :b-active="i === activeBoxIndex"
+              :b-index="parseInt(i)"
+              :b-content="boxes[i].content"
+              :b-action="actionName"
+              :can-delete="canDelete"
+              :suggest-type="'edit'"
+              @onStopResize="changeBoxAttribute($event, i)"
+              @onDelete="deleteBox(i)"
+              @onSelect="makeCurrentBoxActive(i)"
+              @onDisableForm="changeBoxContent($event, i)"
+              @onEnableForm="makeCurrentBoxActive(i)"
+            />
+          </div>
+        </div>
+      </div>
+      <div class="button-block">
+        <div class="btn-close-section">
+          <button
+            type="button"
+            class="btn-label-no-border btn-sm btn-light btn-close-text mt-2"
+            aria-label="Close"
+            @click="closeViewer()"
+          > 
+            <span aria-hidden="true">&times;</span>
+          </button>
         </div>
       </div>
     </div>
-    <div class="btn-section">
-      <button type="button" class="btn-label-no-border btn-lg btn-dark btn-text" @click="saveImage">
+    <div class="btn-save-section">
+      <button
+        type="button"
+        class="btn-label-no-border btn-lg btn-dark btn-text"
+        @click="saveImage"
+      >
         Save Image
       </button>
     </div>
@@ -61,11 +73,13 @@
 
 <script>
 import Box from '~/components/label/Box'
+import Toolbar from '~/components/label/Toolbar'
 import { Cursors } from '~/mixins/label/getCursorPosition'
 
 export default {
   components: {
-    Box
+    Box,
+    Toolbar
   },
   data () {
     return {
@@ -86,6 +100,7 @@ export default {
       },
       canDelete: true,
       labelCount: 0,
+      timer: '',
       dataReady :true,
       isEdited: false
     }
@@ -120,6 +135,21 @@ export default {
         await this.closeViewer()
       }
     },
+    setBoxAction (iconName) {
+      this.actionName = iconName
+      this.activeBoxIndex = -1
+      switch (iconName) {
+      case 'add-box':
+        this.cssCursor = 'cell'
+        break
+      case 'resize-box':
+        this.cssCursor = 'default'
+        break
+      case 'delete-box':
+        this.cssCursor = 'default'
+        break
+      }
+    },
     async getAllLabels () {
       var url = '/api/label/imagequery/' + this.image.id
       var response = await this.$axios.get(url).catch( error => console.error(error))
@@ -164,7 +194,7 @@ export default {
         this.boxes[this.labelCount] = newBox
         // console.log(newBox.content)
         this.changeBoxContent(newBox.content, this.labelCount)
-        this.makeCurrentBoxActive(this.labelCount)
+        // this.makeCurrentBoxActive(this.labelCount)
         this.resetDrawingBox()
       }
       console.log(this.boxes)
@@ -489,7 +519,7 @@ export default {
   }
 
   .viewer-wrapper {
-    height: 87.5vh;
+    /* height: 87.5vh; */
     width: 100vw;
   }
 
@@ -502,10 +532,10 @@ export default {
     justify-content: center;
   }
 
-  .btn-section {
+  .btn-save-section {
     text-align: right;
-    margin-right: 15px;
-    margin-top: -10px;
+    margin-top: -40px;
+    margin-right: 5px;
   }
 
   .btn-text { 
@@ -527,4 +557,18 @@ export default {
     border: 0;
   }
 
+  .btn-lg {
+    width: 115px;
+  }
+
+  .flex-viewer {
+    display: flex;
+    height: 100vh;
+  }
+  
+  .button-block {
+    display: block;
+    height: 100vh;
+  }
+
 </style>
\ No newline at end of file
diff --git a/frontend/pages/viewer/index.vue b/frontend/pages/viewer/index.vue
index f179228..5c6f1c8 100644
--- a/frontend/pages/viewer/index.vue
+++ b/frontend/pages/viewer/index.vue
@@ -10,6 +10,7 @@
             ref="image"
             draggable="false"
             class="image"
+            :style="{ cursor: cssCursor}"
             :src="image.url"
             @mousedown="onMouseDownHandler"
             @mousemove="changeBox"
@@ -25,7 +26,6 @@
           <div v-for="i in Object.keys(boxes)" :key="i">
             <Box
               v-if="boxes[i]"
-              :key="i"
               :b-width="boxes[i].width"
               :b-height="boxes[i].height"
               :b-top="boxes[i].top"
@@ -33,6 +33,7 @@
               :b-active="i === activeBoxIndex"
               :b-index="parseInt(i)"
               :b-content="boxes[i].content"
+              :b-action="actionName"
               :can-delete="canDelete"
               @onStopResize="changeBoxAttribute($event, i)"
               @onDelete="deleteBox(i)"
@@ -88,7 +89,9 @@ export default {
         width: 0,
         content: ''
       },
+      actionName: 'add-box',
       activeBoxIndex: -1,
+      cssCursor: 'cell',
       boxes: {},
       image: {
         id: -1,
@@ -118,7 +121,6 @@ export default {
   methods: {
     async startHeartBeat() {
       var url = '/api/accesscontrol/requestaccess/' + parseInt(this.$route.query.id)
-      // alert(url)
       try {
         await this.$axios.get(url).catch((error) => console.error(error))
       } catch (error) {
@@ -127,7 +129,19 @@ export default {
       }
     },
     setBoxAction (iconName) {
-      console.log("Icon name: ", iconName)
+      this.actionName = iconName
+      this.activeBoxIndex = -1
+      switch (iconName) {
+      case 'add-box':
+        this.cssCursor = 'cell'
+        break
+      case 'resize-box':
+        this.cssCursor = 'default'
+        break
+      case 'delete-box':
+        this.cssCursor = 'default'
+        break
+      }
     },
     async closeViewer () {
       try {
@@ -146,13 +160,14 @@ export default {
       }
     },
     startDrawingBox (e) {
-      console.log("Event: ", e)
-      this.drawingBox = {
-        width: 0,
-        height: 0,
-        left: Cursors.getLeftCursor(e),
-        top: Cursors.getTopCursor(e),
-        active: true
+      if (this.actionName === 'add-box') {
+        this.drawingBox = {
+          width: 0,
+          height: 0,
+          left: Cursors.getLeftCursor(e),
+          top: Cursors.getTopCursor(e),
+          active: true
+        }
       }
     },
     changeBox (e) {
@@ -167,10 +182,11 @@ export default {
       this.boxes[idxBox].top = attribute.bTop
       this.boxes[idxBox].width = attribute.bWidth
       this.boxes[idxBox].height = attribute.bHeight
-      console.log("idxBox: ", this.boxes[idxBox])
     },
     makeCurrentBoxActive (activeBoxIndex) {
-      this.activeBoxIndex = activeBoxIndex
+      if (this.actionName === 'resize-box') {
+        this.activeBoxIndex = activeBoxIndex
+      }
     },
     deleteBox (index) {
       delete this.boxes[index]
@@ -223,7 +239,6 @@ export default {
           try {
             var content_id = await this.createLabelContent(this.boxes[idxBox].content)
             var singleBackendObj = {
-              // TODO: change temporary image_id of 1 to real image_id
               image_id: parseInt(this.image.id),
               label_x_center: realImageAttr.xCenter,
               label_y_center: realImageAttr.yCenter,
@@ -232,14 +247,12 @@ export default {
               label_content_id: content_id
             }
             labelPayload.push(singleBackendObj)
-            console.log('labelPayload: ', labelPayload)
           } catch (error) {
             this.showFailedAlert("Error!", error)
             return
           }
         }
         try {
-          console.log("LABEL PAYLOAD: ", labelPayload)
           await this.createAllLabelsInImage(labelPayload)
           this.showSuccessAlert("Success!", "Image has been saved!").then(async () => {
             await this.closeViewer()
-- 
GitLab