+
>;
+ imageCursorEditing: Readonly[>;
resetMapDimensions(width: number, height: number, margin?: number): void;
}
diff --git a/client/src/components/annotators/useAnnotatorImageCursor.ts b/client/src/components/annotators/useAnnotatorImageCursor.ts
new file mode 100644
index 00000000..4ce2f0af
--- /dev/null
+++ b/client/src/components/annotators/useAnnotatorImageCursor.ts
@@ -0,0 +1,23 @@
+import { computed, Ref } from 'vue';
+
+export default function useAnnotatorImageCursor(
+ imageCursor: Ref,
+ cursor: Ref,
+ imageCursorEditing: Ref,
+) {
+ const displayImageCursor = computed(() => imageCursor.value);
+ const showEditBadge = computed(
+ () => imageCursorEditing.value && !!imageCursor.value,
+ );
+ const showCreateBadge = computed(
+ () => !imageCursorEditing.value && !!imageCursor.value,
+ );
+ const playbackCursor = computed(() => cursor.value);
+
+ return {
+ displayImageCursor,
+ playbackCursor,
+ showEditBadge,
+ showCreateBadge,
+ };
+}
diff --git a/client/src/components/annotators/useMediaController.ts b/client/src/components/annotators/useMediaController.ts
index e48d567b..defa90a0 100644
--- a/client/src/components/annotators/useMediaController.ts
+++ b/client/src/components/annotators/useMediaController.ts
@@ -37,6 +37,7 @@ interface MediaControllerReactiveData {
syncedFrame: number;
cursor: string;
imageCursor: string;
+ imageCursorEditing: boolean;
originalBounds: {
left: number;
top: number;
@@ -213,6 +214,7 @@ export function useMediaController() {
syncedFrame: 0,
cursor: 'default',
imageCursor: '',
+ imageCursorEditing: false,
originalBounds: {
left: 0,
top: 0,
@@ -227,9 +229,10 @@ export function useMediaController() {
}
}
- function setImageCursor(newCursor: string) {
+ function setImageCursor(newCursor: string, editing = false) {
if (state[camera]) {
state[camera].imageCursor = `${newCursor}`;
+ state[camera].imageCursorEditing = editing;
}
}
@@ -417,6 +420,8 @@ export function useMediaController() {
centerOn,
setCursor,
setImageCursor,
+ imageCursor: toRef(state[camera], 'imageCursor'),
+ imageCursorEditing: toRef(state[camera], 'imageCursorEditing'),
setVolume: _setVolume,
setSpeed: _setSpeed,
getController,
diff --git a/client/src/components/index.ts b/client/src/components/index.ts
index de5c2b31..f345e215 100644
--- a/client/src/components/index.ts
+++ b/client/src/components/index.ts
@@ -6,6 +6,7 @@
import ImageAnnotator from './annotators/ImageAnnotator.vue';
import VideoAnnotator from './annotators/VideoAnnotator.vue';
+import AnnotatorImageCursor from './annotators/AnnotatorImageCursor.vue';
import Controls from './controls/Controls.vue';
import EventChart from './controls/EventChart.vue';
@@ -29,8 +30,10 @@ import TypeItem from './TypeItem.vue';
import TypePicker from './TypePicker.vue';
export * from './annotators/useMediaController';
+export { default as useAnnotatorImageCursor } from './annotators/useAnnotatorImageCursor';
export {
/* Annotators */
+ AnnotatorImageCursor,
ImageAnnotator,
VideoAnnotator,
/* Controls */
diff --git a/client/src/layers/EditAnnotationLayer.ts b/client/src/layers/EditAnnotationLayer.ts
index 3b428472..94709210 100644
--- a/client/src/layers/EditAnnotationLayer.ts
+++ b/client/src/layers/EditAnnotationLayer.ts
@@ -304,12 +304,16 @@ export default class EditAnnotationLayer extends BaseLayer {
} else {
this.featureLayer.mode(null);
}
+ this.calculateCursorImage();
}
calculateCursorImage() {
- if (this.getMode() === 'creation') {
- // TODO: we may want to make this more generic or utilize the icons from editMenu
- this.annotator.setImageCursor(`${typeIconMapper.get(this.type)}`);
+ const mode = this.getMode();
+ if (mode === 'creation' || mode === 'editing') {
+ const icon = typeIconMapper.get(this.type);
+ if (icon) {
+ this.annotator.setImageCursor(icon, mode === 'editing');
+ }
}
}
diff --git a/client/src/layers/MediaLayers/maskEditorLayer.ts b/client/src/layers/MediaLayers/maskEditorLayer.ts
index 17432e89..af69e7cb 100644
--- a/client/src/layers/MediaLayers/maskEditorLayer.ts
+++ b/client/src/layers/MediaLayers/maskEditorLayer.ts
@@ -157,6 +157,7 @@ export default class MaskEditorLayer {
this.color = 'transparent';
}
}
+ this.updateImageCursor();
});
this.featureLayer.opacity(editorOptions.opacity.value / 100.0);
watch(editorOptions.opacity, () => {
@@ -293,6 +294,21 @@ export default class MaskEditorLayer {
this.color = 'transparent';
}
}
+ this.updateImageCursor();
+ }
+
+ updateImageCursor() {
+ if (!this.enabled) {
+ return;
+ }
+ const tool = this.editorOptionsRef.toolEnabled.value;
+ if (tool === 'pointer') {
+ this.annotator.setImageCursor('');
+ } else if (tool === 'brush') {
+ this.annotator.setImageCursor('mdi-brush-outline', false);
+ } else if (tool === 'eraser') {
+ this.annotator.setImageCursor('mdi-eraser', true);
+ }
}
drawBrushIcon(x: number, y: number) {
@@ -363,5 +379,6 @@ export default class MaskEditorLayer {
this.trackId = null;
this.frameId = null;
this.enabled = false;
+ this.annotator.setImageCursor('');
}
}
diff --git a/client/src/layers/UILayers/ToolTipLayer.ts b/client/src/layers/UILayers/ToolTipLayer.ts
index 2cd306fc..9c815fc7 100644
--- a/client/src/layers/UILayers/ToolTipLayer.ts
+++ b/client/src/layers/UILayers/ToolTipLayer.ts
@@ -1,6 +1,7 @@
-import Vue from 'vue';
import geo, { GeoEvent } from 'geojs';
+import getVuetify from 'platform/web-girder/plugins/vuetify';
import { MediaController } from '../../components/annotators/mediaControllerType';
+import mountGeoJsWidget from './mountGeoJsWidget';
interface WidgetPosition {
x: number;
@@ -24,26 +25,32 @@ export interface DOMWidget {
* dereferenced inside of the Vue component to properly update.
* This will probably change once Vue 3 is adopted and can be used
*/
-export default class UILayer {
+export default class ToolTipLayer {
annotator: MediaController;
widgets: Record;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ map: any;
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
uiLayer: any;
+ vuetify = getVuetify(undefined);
+
constructor(annotator: MediaController) {
this.annotator = annotator;
this.widgets = {};
- this.uiLayer = this.annotator.geoViewerRef.value.createLayer('ui');
- this.uiLayer.geoOn(geo.event.mousemove, this.updateToolTipPositions);
- this.uiLayer.geoOn(geo.event.zoom, this.zoomToolTipPosition);
+ this.map = this.annotator.geoViewerRef.value;
+ this.uiLayer = this.map.createLayer('ui');
+ this.map.geoOn(geo.event.mousemove, this.updateToolTipPositions);
+ this.map.geoOn(geo.event.zoom, this.zoomToolTipPosition);
}
updateWidgetToolTipPosition(mousePos: WidgetPosition, widget: DOMWidget) {
const tipOffset = widget.toolTipOffset;
- const newOffset = this.uiLayer.map().gcsToDisplay(mousePos);
- const finalOffset = this.uiLayer.map().displayToGcs(
+ const newOffset = this.map.gcsToDisplay(mousePos);
+ const finalOffset = this.map.displayToGcs(
{
x: newOffset.x + tipOffset.x, y: newOffset.y + tipOffset.y,
},
@@ -66,6 +73,9 @@ export default class UILayer {
if (this.widgets[name].toolTip) {
this.updateWidgetToolTipPosition(mousePos, this.widgets[name]);
this.widgets[name].lastMousePos = mousePos;
+ this.widgets[name].canvas().style.display = 'block';
+ } else {
+ this.widgets[name].canvas().style.display = 'none';
}
});
};
@@ -81,11 +91,13 @@ export default class UILayer {
const widget: DOMWidget = this.uiLayer.createWidget('dom', { position });
widget.canvas().setAttribute('id', name);
const parent = widget.canvas();
+ parent.style.pointerEvents = 'none';
+ parent.style.overflow = 'visible';
+ parent.style.background = 'transparent';
+ parent.style.display = 'none';
const div = document.createElement('div');
const element = parent.appendChild(div);
- new Vue({
- render: (h) => h(component, { props }),
- }).$mount(element);
+ mountGeoJsWidget(element, component, props, this.vuetify);
widget.toolTipOffset = position;
widget.toolTip = false;
widget.lastMousePos = position;
@@ -97,6 +109,7 @@ export default class UILayer {
setToolTipWidget(name: string, on: boolean) {
if (this.widgets[name]) {
this.widgets[name].toolTip = on;
+ this.widgets[name].canvas().style.display = on ? 'block' : 'none';
}
}
}
diff --git a/client/src/layers/UILayers/ToolTipWidget.vue b/client/src/layers/UILayers/ToolTipWidget.vue
index 8559483c..88c338b5 100644
--- a/client/src/layers/UILayers/ToolTipWidget.vue
+++ b/client/src/layers/UILayers/ToolTipWidget.vue
@@ -1,6 +1,6 @@
]