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">×</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">×</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