diff --git a/README.md b/README.md index 507b3a14..6805f47c 100644 --- a/README.md +++ b/README.md @@ -96,8 +96,11 @@ docker logs make_sense | Load next image | Editor | + Right | Ctrl + Right | | Zoom in | Editor | + + | Ctrl + + | | Zoom out | Editor | + - | Ctrl + - | +| Toggle Drag Mode | Editor | h | h | | Move image | Editor | Up / Down / Left / Right | Up / Down / Left / Right | | Select Label | Editor | + 0-9 | Ctrl + 0-9 | +| Copy all labels | Editor | + c | Ctrl + c | +| Paste all labels | Editor | + p | Ctrl + p | | Exit popup | Popup | Escape | Escape | **Table 1.** Supported keyboard shortcuts diff --git a/src/logic/context/EditorContext.ts b/src/logic/context/EditorContext.ts index 14ee56ae..9b39ab38 100644 --- a/src/logic/context/EditorContext.ts +++ b/src/logic/context/EditorContext.ts @@ -167,6 +167,24 @@ export class EditorContext extends BaseContext { ImageActions.setActiveLabelOnActiveImage(9); EditorActions.fullRender(); } + }, + { + keyCombo: ["h"], + action: (event: KeyboardEvent) =>{ + document.getElementById("imageDragOnClick").click(); + } + }, + { + keyCombo: PlatformUtil.isMac(window.navigator.userAgent) ? ["Alt", "v"] : ["Control", "v"], + action: (event: KeyboardEvent)=>{ + EditorModel.supportRenderingEngine.pasteHandler(); + } + }, + { + keyCombo: PlatformUtil.isMac(window.navigator.userAgent) ? ["Alt", "v"] : ["Control", "c"], + action: (event: KeyboardEvent)=>{ + EditorModel.supportRenderingEngine.copyHandler(); + } } ]; } \ No newline at end of file diff --git a/src/logic/render/BaseRenderEngine.ts b/src/logic/render/BaseRenderEngine.ts index 1f07b9ae..f41e0048 100644 --- a/src/logic/render/BaseRenderEngine.ts +++ b/src/logic/render/BaseRenderEngine.ts @@ -4,12 +4,16 @@ import {EventType} from '../../data/enums/EventType'; import {LabelType} from '../../data/enums/LabelType'; import {GeneralSelector} from '../../store/selectors/GeneralSelector'; import {RenderEngineSettings} from '../../settings/RenderEngineSettings'; -import {LabelName} from '../../store/labels/types'; +import {ImageData, LabelName} from '../../store/labels/types'; import {LabelsSelector} from '../../store/selectors/LabelsSelector'; +import { EditorModel } from '../../staticModels/EditorModel'; +import { PolygonRenderEngine } from './PolygonRenderEngine'; +import { LineRenderEngine } from './LineRenderEngine'; export abstract class BaseRenderEngine { protected readonly canvas: HTMLCanvasElement; public labelType: LabelType; + protected imageDataCache: ImageData; protected constructor(canvas: HTMLCanvasElement) { this.canvas = canvas; @@ -36,6 +40,20 @@ export abstract class BaseRenderEngine { protected abstract mouseDownHandler(data: EditorData): void; protected abstract mouseMoveHandler(data: EditorData): void; protected abstract mouseUpHandler(data: EditorData): void; + public abstract pasteHandler(): void; + + public copyHandler(){ + switch (EditorModel.supportRenderingEngine.labelType) { + case LabelType.POLYGON: + (EditorModel.supportRenderingEngine as PolygonRenderEngine).cancelLabelCreation(); + break; + case LabelType.LINE: + (EditorModel.supportRenderingEngine as LineRenderEngine).cancelLabelCreation(); + break; + } + const imageData = LabelsSelector.getActiveImageData(); + this.imageDataCache = {...imageData}; + } abstract render(data: EditorData): void; diff --git a/src/logic/render/LineRenderEngine.ts b/src/logic/render/LineRenderEngine.ts index e9d09534..b393403e 100644 --- a/src/logic/render/LineRenderEngine.ts +++ b/src/logic/render/LineRenderEngine.ts @@ -84,6 +84,21 @@ export class LineRenderEngine extends BaseRenderEngine { } } } + + public pasteHandler(): void { + this.cancelLabelCreation(); + if(this.imageDataCache && this.imageDataCache.labelLines){ + const imageData: ImageData = LabelsSelector.getActiveImageData(); + this.imageDataCache.labelLines.forEach((lineLabel) => { + imageData.labelLines.push(lineLabel); + store.dispatch(updateImageDataById(imageData.id, imageData)); + store.dispatch(updateFirstLabelCreatedFlag(true)); + store.dispatch(updateActiveLabelId(lineLabel.id)); + this.lineCreationStartPoint = null + }); + EditorActions.setViewPortActionsDisabledStatus(false); + } + } // ================================================================================================================= // RENDERING diff --git a/src/logic/render/PointRenderEngine.ts b/src/logic/render/PointRenderEngine.ts index 727f0739..41583ffa 100644 --- a/src/logic/render/PointRenderEngine.ts +++ b/src/logic/render/PointRenderEngine.ts @@ -7,6 +7,7 @@ import {ImageData, LabelPoint} from '../../store/labels/types'; import { v4 as uuidv4 } from 'uuid'; import { updateActiveLabelId, + updateActiveLabelNameId, updateFirstLabelCreatedFlag, updateHighlightedLabelId, updateImageDataById @@ -103,6 +104,15 @@ export class PointRenderEngine extends BaseRenderEngine { } } } + + public pasteHandler(): void { + if(this.imageDataCache && this.imageDataCache.labelPoints){ + this.imageDataCache.labelPoints.forEach((pointLabel) => { + store.dispatch(updateActiveLabelNameId(pointLabel.labelId)); + this.addPointLabel(pointLabel.point); + }); + } + } // ================================================================================================================= // RENDERING diff --git a/src/logic/render/PolygonRenderEngine.ts b/src/logic/render/PolygonRenderEngine.ts index b9091f9e..d9bf134c 100644 --- a/src/logic/render/PolygonRenderEngine.ts +++ b/src/logic/render/PolygonRenderEngine.ts @@ -13,6 +13,7 @@ import {ImageData, LabelPolygon} from '../../store/labels/types'; import {LabelsSelector} from '../../store/selectors/LabelsSelector'; import { updateActiveLabelId, + updateActiveLabelNameId, updateFirstLabelCreatedFlag, updateHighlightedLabelId, updateImageDataById @@ -28,7 +29,7 @@ import {Settings} from '../../settings/Settings'; import {LabelUtil} from '../../utils/LabelUtil'; export class PolygonRenderEngine extends BaseRenderEngine { - + // ================================================================================================================= // STATE // ================================================================================================================= @@ -144,6 +145,15 @@ export class PolygonRenderEngine extends BaseRenderEngine { } } } + + public pasteHandler(): void { + if(this.imageDataCache && this.imageDataCache.labelPolygons){ + this.imageDataCache.labelPolygons.forEach((polyLabel) => { + store.dispatch(updateActiveLabelNameId(polyLabel.labelId)); + this.addPolygonLabel(polyLabel.vertices); + }); + } + } // ================================================================================================================= // RENDERING diff --git a/src/logic/render/PrimaryEditorRenderEngine.ts b/src/logic/render/PrimaryEditorRenderEngine.ts index a7685543..ad245b83 100644 --- a/src/logic/render/PrimaryEditorRenderEngine.ts +++ b/src/logic/render/PrimaryEditorRenderEngine.ts @@ -24,6 +24,7 @@ export class PrimaryEditorRenderEngine extends BaseRenderEngine { public mouseMoveHandler(data: EditorData): void {} public mouseDownHandler(data: EditorData): void {} public mouseUpHandler(data: EditorData): void {} + public pasteHandler(): void {} // ================================================================================================================= // RENDERING diff --git a/src/logic/render/RectRenderEngine.ts b/src/logic/render/RectRenderEngine.ts index 0a6b8e21..76b221fa 100644 --- a/src/logic/render/RectRenderEngine.ts +++ b/src/logic/render/RectRenderEngine.ts @@ -1,32 +1,32 @@ -import {IPoint} from '../../interfaces/IPoint'; -import {IRect} from '../../interfaces/IRect'; -import {RectUtil} from '../../utils/RectUtil'; -import {DrawUtil} from '../../utils/DrawUtil'; -import {store} from '../..'; -import {ImageData, LabelRect} from '../../store/labels/types'; +import { IPoint } from '../../interfaces/IPoint'; +import { IRect } from '../../interfaces/IRect'; +import { RectUtil } from '../../utils/RectUtil'; +import { DrawUtil } from '../../utils/DrawUtil'; +import { store } from '../..'; +import { ImageData, LabelRect } from '../../store/labels/types'; import { updateActiveLabelId, + updateActiveLabelNameId, updateFirstLabelCreatedFlag, updateHighlightedLabelId, updateImageDataById } from '../../store/labels/actionCreators'; -import {PointUtil} from '../../utils/PointUtil'; -import {RectAnchor} from '../../data/RectAnchor'; -import {RenderEngineSettings} from '../../settings/RenderEngineSettings'; -import {updateCustomCursorStyle} from '../../store/general/actionCreators'; -import {CustomCursorStyle} from '../../data/enums/CustomCursorStyle'; -import {LabelsSelector} from '../../store/selectors/LabelsSelector'; -import {EditorData} from '../../data/EditorData'; -import {BaseRenderEngine} from './BaseRenderEngine'; -import {RenderEngineUtil} from '../../utils/RenderEngineUtil'; -import {LabelType} from '../../data/enums/LabelType'; -import {EditorActions} from '../actions/EditorActions'; -import {GeneralSelector} from '../../store/selectors/GeneralSelector'; -import {LabelStatus} from '../../data/enums/LabelStatus'; -import {LabelUtil} from '../../utils/LabelUtil'; +import { PointUtil } from '../../utils/PointUtil'; +import { RectAnchor } from '../../data/RectAnchor'; +import { RenderEngineSettings } from '../../settings/RenderEngineSettings'; +import { updateCustomCursorStyle } from '../../store/general/actionCreators'; +import { CustomCursorStyle } from '../../data/enums/CustomCursorStyle'; +import { LabelsSelector } from '../../store/selectors/LabelsSelector'; +import { EditorData } from '../../data/EditorData'; +import { BaseRenderEngine } from './BaseRenderEngine'; +import { RenderEngineUtil } from '../../utils/RenderEngineUtil'; +import { LabelType } from '../../data/enums/LabelType'; +import { EditorActions } from '../actions/EditorActions'; +import { GeneralSelector } from '../../store/selectors/GeneralSelector'; +import { LabelStatus } from '../../data/enums/LabelStatus'; +import { LabelUtil } from '../../utils/LabelUtil'; export class RectRenderEngine extends BaseRenderEngine { - // ================================================================================================================= // STATE // ================================================================================================================= @@ -49,19 +49,30 @@ export class RectRenderEngine extends BaseRenderEngine { if (isMouseOverCanvas) { const rectUnderMouse: LabelRect = this.getRectUnderMouse(data); if (!!rectUnderMouse) { - const rect: IRect = this.calculateRectRelativeToActiveImage(rectUnderMouse.rect, data); - const anchorUnderMouse: RectAnchor = this.getAnchorUnderMouseByRect(rect, data.mousePositionOnViewPortContent, data.viewPortContentImageRect); - if (!!anchorUnderMouse && rectUnderMouse.status === LabelStatus.ACCEPTED) { + const rect: IRect = this.calculateRectRelativeToActiveImage( + rectUnderMouse.rect, + data + ); + const anchorUnderMouse: RectAnchor = this.getAnchorUnderMouseByRect( + rect, + data.mousePositionOnViewPortContent, + data.viewPortContentImageRect + ); + if ( + !!anchorUnderMouse && + rectUnderMouse.status === LabelStatus.ACCEPTED + ) { store.dispatch(updateActiveLabelId(rectUnderMouse.id)); this.startRectResize(anchorUnderMouse); } else { - if (!!LabelsSelector.getHighlightedLabelId()) - store.dispatch(updateActiveLabelId(LabelsSelector.getHighlightedLabelId())); - else - this.startRectCreation(data.mousePositionOnViewPortContent); + if (!!LabelsSelector.getHighlightedLabelId()) { + console.log(LabelsSelector.getHighlightedLabelId()); + store.dispatch( + updateActiveLabelId(LabelsSelector.getHighlightedLabelId()) + ); + } else this.startRectCreation(data.mousePositionOnViewPortContent); } } else if (isMouseOverImage) { - this.startRectCreation(data.mousePositionOnViewPortContent); } } @@ -69,63 +80,113 @@ export class RectRenderEngine extends BaseRenderEngine { public mouseUpHandler = (data: EditorData) => { if (!!data.viewPortContentImageRect) { - const mousePositionSnapped: IPoint = RectUtil.snapPointToRect(data.mousePositionOnViewPortContent, data.viewPortContentImageRect); + const mousePositionSnapped: IPoint = RectUtil.snapPointToRect( + data.mousePositionOnViewPortContent, + data.viewPortContentImageRect + ); const activeLabelRect: LabelRect = LabelsSelector.getActiveRectLabel(); - if (!!this.startCreateRectPoint && !PointUtil.equals(this.startCreateRectPoint, mousePositionSnapped)) { - - const minX: number = Math.min(this.startCreateRectPoint.x, mousePositionSnapped.x); - const minY: number = Math.min(this.startCreateRectPoint.y, mousePositionSnapped.y); - const maxX: number = Math.max(this.startCreateRectPoint.x, mousePositionSnapped.x); - const maxY: number = Math.max(this.startCreateRectPoint.y, mousePositionSnapped.y); - - const rect = {x: minX, y: minY, width: maxX - minX, height: maxY - minY}; - this.addRectLabel(RenderEngineUtil.transferRectFromImageToViewPortContent(rect, data)); + if ( + !!this.startCreateRectPoint && + !PointUtil.equals(this.startCreateRectPoint, mousePositionSnapped) + ) { + const minX: number = Math.min( + this.startCreateRectPoint.x, + mousePositionSnapped.x + ); + const minY: number = Math.min( + this.startCreateRectPoint.y, + mousePositionSnapped.y + ); + const maxX: number = Math.max( + this.startCreateRectPoint.x, + mousePositionSnapped.x + ); + const maxY: number = Math.max( + this.startCreateRectPoint.y, + mousePositionSnapped.y + ); + + const rect = { + x: minX, + y: minY, + width: maxX - minX, + height: maxY - minY + }; + this.addRectLabel( + RenderEngineUtil.transferRectFromImageToViewPortContent(rect, data) + ); } if (!!this.startResizeRectAnchor && !!activeLabelRect) { - const rect: IRect = this.calculateRectRelativeToActiveImage(activeLabelRect.rect, data); - const startAnchorPosition: IPoint = PointUtil.add(this.startResizeRectAnchor.position, - data.viewPortContentImageRect); - const delta: IPoint = PointUtil.subtract(mousePositionSnapped, startAnchorPosition); - const resizeRect: IRect = RectUtil.resizeRect(rect, this.startResizeRectAnchor.type, delta); + const rect: IRect = this.calculateRectRelativeToActiveImage( + activeLabelRect.rect, + data + ); + const startAnchorPosition: IPoint = PointUtil.add( + this.startResizeRectAnchor.position, + data.viewPortContentImageRect + ); + const delta: IPoint = PointUtil.subtract( + mousePositionSnapped, + startAnchorPosition + ); + const resizeRect: IRect = RectUtil.resizeRect( + rect, + this.startResizeRectAnchor.type, + delta + ); const scale: number = RenderEngineUtil.calculateImageScale(data); const scaledRect: IRect = RectUtil.scaleRect(resizeRect, scale); const imageData = LabelsSelector.getActiveImageData(); - imageData.labelRects = imageData.labelRects.map((labelRect: LabelRect) => { - if (labelRect.id === activeLabelRect.id) { - return { - ...labelRect, - rect: scaledRect - }; + imageData.labelRects = imageData.labelRects.map( + (labelRect: LabelRect) => { + if (labelRect.id === activeLabelRect.id) { + return { + ...labelRect, + rect: scaledRect + }; + } + return labelRect; } - return labelRect; - }); + ); store.dispatch(updateImageDataById(imageData.id, imageData)); } } - this.endRectTransformation() + this.endRectTransformation(); }; public mouseMoveHandler = (data: EditorData) => { - if (!!data.viewPortContentImageRect && !!data.mousePositionOnViewPortContent) { + if ( + !!data.viewPortContentImageRect && + !!data.mousePositionOnViewPortContent + ) { const isOverImage: boolean = RenderEngineUtil.isMouseOverImage(data); if (isOverImage && !this.startResizeRectAnchor) { const labelRect: LabelRect = this.getRectUnderMouse(data); if (!!labelRect && !this.isInProgress()) { if (LabelsSelector.getHighlightedLabelId() !== labelRect.id) { - store.dispatch(updateHighlightedLabelId(labelRect.id)) + store.dispatch(updateHighlightedLabelId(labelRect.id)); } } else { if (LabelsSelector.getHighlightedLabelId() !== null) { - store.dispatch(updateHighlightedLabelId(null)) + store.dispatch(updateHighlightedLabelId(null)); } } } } }; + public pasteHandler(): void { + if (this.imageDataCache && this.imageDataCache.labelRects) { + this.imageDataCache.labelRects.forEach((rectLabel) => { + store.dispatch(updateActiveLabelNameId(rectLabel.labelId)); + this.addRectLabel(rectLabel.rect); + }); + } + } + // ================================================================================================================= // RENDERING // ================================================================================================================= @@ -135,82 +196,163 @@ export class RectRenderEngine extends BaseRenderEngine { const imageData: ImageData = LabelsSelector.getActiveImageData(); if (imageData) { imageData.labelRects.forEach((labelRect: LabelRect) => { - if (labelRect.status === LabelStatus.ACCEPTED && labelRect.id === activeLabelId) { - this.drawActiveRect(labelRect, data) + if ( + labelRect.status === LabelStatus.ACCEPTED && + labelRect.id === activeLabelId + ) { + this.drawActiveRect(labelRect, data); } else { this.drawInactiveRect(labelRect, data); } }); - this.drawCurrentlyCreatedRect(data.mousePositionOnViewPortContent, data.viewPortContentImageRect); + this.drawCurrentlyCreatedRect( + data.mousePositionOnViewPortContent, + data.viewPortContentImageRect + ); this.updateCursorStyle(data); } } private drawCurrentlyCreatedRect(mousePosition: IPoint, imageRect: IRect) { if (!!this.startCreateRectPoint) { - const mousePositionSnapped: IPoint = RectUtil.snapPointToRect(mousePosition, imageRect); + const mousePositionSnapped: IPoint = RectUtil.snapPointToRect( + mousePosition, + imageRect + ); const activeRect: IRect = { x: this.startCreateRectPoint.x, y: this.startCreateRectPoint.y, width: mousePositionSnapped.x - this.startCreateRectPoint.x, height: mousePositionSnapped.y - this.startCreateRectPoint.y }; - const activeRectBetweenPixels = RenderEngineUtil.setRectBetweenPixels(activeRect); - const lineColor: string = BaseRenderEngine.resolveLabelLineColor(null, true) - DrawUtil.drawRect(this.canvas, activeRectBetweenPixels, lineColor, RenderEngineSettings.LINE_THICKNESS); + const activeRectBetweenPixels = + RenderEngineUtil.setRectBetweenPixels(activeRect); + const lineColor: string = BaseRenderEngine.resolveLabelLineColor( + null, + true + ); + DrawUtil.drawRect( + this.canvas, + activeRectBetweenPixels, + lineColor, + RenderEngineSettings.LINE_THICKNESS + ); } } private drawInactiveRect(labelRect: LabelRect, data: EditorData) { - const rectOnImage: IRect = RenderEngineUtil.transferRectFromViewPortContentToImage(labelRect.rect, data) - const highlightedLabelId: string = LabelsSelector.getHighlightedLabelId() - const displayAsActive: boolean = labelRect.status === LabelStatus.ACCEPTED && labelRect.id === highlightedLabelId; - const lineColor: string = BaseRenderEngine.resolveLabelLineColor(labelRect.labelId, displayAsActive) - const anchorColor: string = BaseRenderEngine.resolveLabelAnchorColor(displayAsActive); + const rectOnImage: IRect = + RenderEngineUtil.transferRectFromViewPortContentToImage( + labelRect.rect, + data + ); + const highlightedLabelId: string = LabelsSelector.getHighlightedLabelId(); + const displayAsActive: boolean = + labelRect.status === LabelStatus.ACCEPTED && + labelRect.id === highlightedLabelId; + const lineColor: string = BaseRenderEngine.resolveLabelLineColor( + labelRect.labelId, + displayAsActive + ); + const anchorColor: string = + BaseRenderEngine.resolveLabelAnchorColor(displayAsActive); this.renderRect(rectOnImage, displayAsActive, lineColor, anchorColor); } private drawActiveRect(labelRect: LabelRect, data: EditorData) { - let rect: IRect = this.calculateRectRelativeToActiveImage(labelRect.rect, data); + let rect: IRect = this.calculateRectRelativeToActiveImage( + labelRect.rect, + data + ); if (!!this.startResizeRectAnchor) { - const startAnchorPosition: IPoint = PointUtil.add(this.startResizeRectAnchor.position, data.viewPortContentImageRect); - const endAnchorPositionSnapped: IPoint = RectUtil.snapPointToRect(data.mousePositionOnViewPortContent, data.viewPortContentImageRect); - const delta = PointUtil.subtract(endAnchorPositionSnapped, startAnchorPosition); + const startAnchorPosition: IPoint = PointUtil.add( + this.startResizeRectAnchor.position, + data.viewPortContentImageRect + ); + const endAnchorPositionSnapped: IPoint = RectUtil.snapPointToRect( + data.mousePositionOnViewPortContent, + data.viewPortContentImageRect + ); + const delta = PointUtil.subtract( + endAnchorPositionSnapped, + startAnchorPosition + ); rect = RectUtil.resizeRect(rect, this.startResizeRectAnchor.type, delta); } - const rectOnImage: IRect = RectUtil.translate(rect, data.viewPortContentImageRect); - const lineColor: string = BaseRenderEngine.resolveLabelLineColor(labelRect.labelId, true) + const rectOnImage: IRect = RectUtil.translate( + rect, + data.viewPortContentImageRect + ); + const lineColor: string = BaseRenderEngine.resolveLabelLineColor( + labelRect.labelId, + true + ); const anchorColor: string = BaseRenderEngine.resolveLabelAnchorColor(true); this.renderRect(rectOnImage, true, lineColor, anchorColor); } - private renderRect(rectOnImage: IRect, isActive: boolean, lineColor: string, anchorColor: string) { - const rectBetweenPixels = RenderEngineUtil.setRectBetweenPixels(rectOnImage); - DrawUtil.drawRectWithFill(this.canvas, rectBetweenPixels, DrawUtil.hexToRGB(lineColor, 0.2)); - DrawUtil.drawRect(this.canvas, rectBetweenPixels, lineColor, RenderEngineSettings.LINE_THICKNESS); + private renderRect( + rectOnImage: IRect, + isActive: boolean, + lineColor: string, + anchorColor: string + ) { + const rectBetweenPixels = + RenderEngineUtil.setRectBetweenPixels(rectOnImage); + DrawUtil.drawRectWithFill( + this.canvas, + rectBetweenPixels, + DrawUtil.hexToRGB(lineColor, 0.2) + ); + DrawUtil.drawRect( + this.canvas, + rectBetweenPixels, + lineColor, + RenderEngineSettings.LINE_THICKNESS + ); if (isActive) { - const handleCenters: IPoint[] = RectUtil.mapRectToAnchors(rectOnImage).map((rectAnchor: RectAnchor) => rectAnchor.position); + const handleCenters: IPoint[] = RectUtil.mapRectToAnchors( + rectOnImage + ).map((rectAnchor: RectAnchor) => rectAnchor.position); handleCenters.forEach((center: IPoint) => { - const handleRect: IRect = RectUtil.getRectWithCenterAndSize(center, RenderEngineSettings.anchorSize); - const handleRectBetweenPixels: IRect = RenderEngineUtil.setRectBetweenPixels(handleRect); - DrawUtil.drawRectWithFill(this.canvas, handleRectBetweenPixels, anchorColor); - }) + const handleRect: IRect = RectUtil.getRectWithCenterAndSize( + center, + RenderEngineSettings.anchorSize + ); + const handleRectBetweenPixels: IRect = + RenderEngineUtil.setRectBetweenPixels(handleRect); + DrawUtil.drawRectWithFill( + this.canvas, + handleRectBetweenPixels, + anchorColor + ); + }); } } private updateCursorStyle(data: EditorData) { - if (!!this.canvas && !!data.mousePositionOnViewPortContent && !GeneralSelector.getImageDragModeStatus()) { + if ( + !!this.canvas && + !!data.mousePositionOnViewPortContent && + !GeneralSelector.getImageDragModeStatus() + ) { const rectUnderMouse: LabelRect = this.getRectUnderMouse(data); const rectAnchorUnderMouse: RectAnchor = this.getAnchorUnderMouse(data); - if ((!!rectAnchorUnderMouse && rectUnderMouse && rectUnderMouse.status === LabelStatus.ACCEPTED) || !!this.startResizeRectAnchor) { + if ( + (!!rectAnchorUnderMouse && + rectUnderMouse && + rectUnderMouse.status === LabelStatus.ACCEPTED) || + !!this.startResizeRectAnchor + ) { store.dispatch(updateCustomCursorStyle(CustomCursorStyle.MOVE)); return; - } - else if (RenderEngineUtil.isMouseOverCanvas(data)) { - if (!RenderEngineUtil.isMouseOverImage(data) && !!this.startCreateRectPoint) + } else if (RenderEngineUtil.isMouseOverCanvas(data)) { + if ( + !RenderEngineUtil.isMouseOverImage(data) && + !!this.startCreateRectPoint + ) store.dispatch(updateCustomCursorStyle(CustomCursorStyle.MOVE)); - else - RenderEngineUtil.wrapDefaultCursorStyleInCancel(data); + else RenderEngineUtil.wrapDefaultCursorStyleInCancel(data); this.canvas.style.cursor = 'none'; } else { this.canvas.style.cursor = 'default'; @@ -226,9 +368,12 @@ export class RectRenderEngine extends BaseRenderEngine { return !!this.startCreateRectPoint || !!this.startResizeRectAnchor; } - private calculateRectRelativeToActiveImage(rect: IRect, data: EditorData):IRect { + private calculateRectRelativeToActiveImage( + rect: IRect, + data: EditorData + ): IRect { const scale: number = RenderEngineUtil.calculateImageScale(data); - return RectUtil.scaleRect(rect, 1/scale); + return RectUtil.scaleRect(rect, 1 / scale); } private addRectLabel = (rect: IRect) => { @@ -243,11 +388,15 @@ export class RectRenderEngine extends BaseRenderEngine { private getRectUnderMouse(data: EditorData): LabelRect { const activeRectLabel: LabelRect = LabelsSelector.getActiveRectLabel(); - if (!!activeRectLabel && this.isMouseOverRectEdges(activeRectLabel.rect, data)) { + if ( + !!activeRectLabel && + this.isMouseOverRectEdges(activeRectLabel.rect, data) + ) { return activeRectLabel; } - const labelRects: LabelRect[] = LabelsSelector.getActiveImageData().labelRects; + const labelRects: LabelRect[] = + LabelsSelector.getActiveImageData().labelRects; for (let i = 0; i < labelRects.length; i++) { if (this.isMouseOverRectEdges(labelRects[i].rect, data)) { return labelRects[i]; @@ -258,7 +407,9 @@ export class RectRenderEngine extends BaseRenderEngine { private isMouseOverRectEdges(rect: IRect, data: EditorData): boolean { const rectOnImage: IRect = RectUtil.translate( - this.calculateRectRelativeToActiveImage(rect, data), data.viewPortContentImageRect); + this.calculateRectRelativeToActiveImage(rect, data), + data.viewPortContentImageRect + ); const outerRectDelta: IPoint = { x: RenderEngineSettings.anchorHoverSize.width / 2, @@ -267,20 +418,35 @@ export class RectRenderEngine extends BaseRenderEngine { const outerRect: IRect = RectUtil.expand(rectOnImage, outerRectDelta); const innerRectDelta: IPoint = { - x: - RenderEngineSettings.anchorHoverSize.width / 2, - y: - RenderEngineSettings.anchorHoverSize.height / 2 + x: -RenderEngineSettings.anchorHoverSize.width / 2, + y: -RenderEngineSettings.anchorHoverSize.height / 2 }; const innerRect: IRect = RectUtil.expand(rectOnImage, innerRectDelta); - return (RectUtil.isPointInside(outerRect, data.mousePositionOnViewPortContent) && - !RectUtil.isPointInside(innerRect, data.mousePositionOnViewPortContent)); + return ( + RectUtil.isPointInside(outerRect, data.mousePositionOnViewPortContent) && + !RectUtil.isPointInside(innerRect, data.mousePositionOnViewPortContent) + ); } - private getAnchorUnderMouseByRect(rect: IRect, mousePosition: IPoint, imageRect: IRect): RectAnchor { + private getAnchorUnderMouseByRect( + rect: IRect, + mousePosition: IPoint, + imageRect: IRect + ): RectAnchor { const rectAnchors: RectAnchor[] = RectUtil.mapRectToAnchors(rect); for (let i = 0; i < rectAnchors.length; i++) { - const anchorRect: IRect = RectUtil.translate(RectUtil.getRectWithCenterAndSize(rectAnchors[i].position, RenderEngineSettings.anchorHoverSize), imageRect); - if (!!mousePosition && RectUtil.isPointInside(anchorRect, mousePosition)) { + const anchorRect: IRect = RectUtil.translate( + RectUtil.getRectWithCenterAndSize( + rectAnchors[i].position, + RenderEngineSettings.anchorHoverSize + ), + imageRect + ); + if ( + !!mousePosition && + RectUtil.isPointInside(anchorRect, mousePosition) + ) { return rectAnchors[i]; } } @@ -288,10 +454,18 @@ export class RectRenderEngine extends BaseRenderEngine { } private getAnchorUnderMouse(data: EditorData): RectAnchor { - const labelRects: LabelRect[] = LabelsSelector.getActiveImageData().labelRects; + const labelRects: LabelRect[] = + LabelsSelector.getActiveImageData().labelRects; for (let i = 0; i < labelRects.length; i++) { - const rect: IRect = this.calculateRectRelativeToActiveImage(labelRects[i].rect, data); - const rectAnchor = this.getAnchorUnderMouseByRect(rect, data.mousePositionOnViewPortContent, data.viewPortContentImageRect); + const rect: IRect = this.calculateRectRelativeToActiveImage( + labelRects[i].rect, + data + ); + const rectAnchor = this.getAnchorUnderMouseByRect( + rect, + data.mousePositionOnViewPortContent, + data.viewPortContentImageRect + ); if (!!rectAnchor) return rectAnchor; } return null; diff --git a/src/views/Common/ImageButton/ImageButton.tsx b/src/views/Common/ImageButton/ImageButton.tsx index 4015dc2f..385a75e0 100644 --- a/src/views/Common/ImageButton/ImageButton.tsx +++ b/src/views/Common/ImageButton/ImageButton.tsx @@ -18,7 +18,7 @@ export interface ImageButtonProps extends React.HTMLProps { } export const ImageButton = React.forwardRef((props: ImageButtonProps, ref: LegacyRef) => { - const {buttonSize, padding, image, imageAlt, href, onClick, style, isActive, isDisabled, externalClassName} = props; + const {buttonSize, padding, image, imageAlt, href, onClick, id, style, isActive, isDisabled, externalClassName} = props; const imagePadding:number = !!padding ? padding : 10; const onClickHandler = (event: React.MouseEvent) => { @@ -54,6 +54,7 @@ export const ImageButton = React.forwardRef((props: ImageButtonProps, ref: Legac style={buttonStyle} onClick={onClickHandler} ref={ref} + id={id} > {!!href && any + onClick?:() => any, + id?:string ): React.ReactElement => { return @@ -160,7 +162,8 @@ const EditorTopNavigationBar: React.FC = ( 'image-drag-mode', imageDragMode, undefined, - imageDragOnClick + imageDragOnClick, + 'imageDragOnClick' ) } { diff --git a/src/views/EditorView/EditorView.tsx b/src/views/EditorView/EditorView.tsx index c016720b..cb10a7d9 100644 --- a/src/views/EditorView/EditorView.tsx +++ b/src/views/EditorView/EditorView.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import './EditorView.scss'; import EditorContainer from './EditorContainer/EditorContainer'; import {PopupWindowType} from '../../data/enums/PopupWindowType'; @@ -13,6 +13,12 @@ interface IProps { const EditorView: React.FC = ({activePopupType}) => { + // Disable middle mouse scroll + useEffect(() => { + document.body.onmousedown = e => { return e.button === 1 ? false : true; }; + }, [] + ); + const getClassName = () => { return classNames( 'EditorView',