fix: Clicking on measurement should highlight the measurement row in the measurement panel OHIF-433 (#3111)
* fix: OHIF-433 Clicking on measurement should highlight the measurement row in the measurement panel Added selected flag to each OHIF measurement. Listening to CS3D ANNOTATION_SELECTION_CHANGE to keep the selected flag in sync. * fix: OHIF-433 Clicking on measurement should highlight the measurement row in the measurement panel Fixed broken unit tests. * PR feedback from Alireza: - use MEASUREMENT_UPDATED event for selection changed - added TODO to eventually have CS3D introduce the selected flag per annotation - added MeasurementService._publishEvent * PR feedback: now using _broadcastEvent
This commit is contained in:
parent
a997198587
commit
58ae8690a7
@ -121,11 +121,36 @@ const connectToolsToMeasurementService = (
|
||||
const { toolName } = metadata;
|
||||
|
||||
annotationModifiedEventDetail.uid = annotationUID;
|
||||
annotationToMeasurement(toolName, annotationModifiedEventDetail);
|
||||
// Passing true to indicate this is an update and NOT a annotation (start) completion.
|
||||
annotationToMeasurement(toolName, annotationModifiedEventDetail, true);
|
||||
} catch (error) {
|
||||
console.warn('Failed to update measurement:', error);
|
||||
}
|
||||
}
|
||||
function selectMeasurement(csToolsEvent) {
|
||||
try {
|
||||
const annotationSelectionEventDetail = csToolsEvent.detail;
|
||||
|
||||
const {
|
||||
added: addedSelectedAnnotationUIDs,
|
||||
removed: removedSelectedAnnotationUIDs,
|
||||
} = annotationSelectionEventDetail;
|
||||
|
||||
if (removedSelectedAnnotationUIDs) {
|
||||
removedSelectedAnnotationUIDs.forEach(annotationUID =>
|
||||
MeasurementService.setMeasurementSelected(annotationUID, false)
|
||||
);
|
||||
}
|
||||
|
||||
if (addedSelectedAnnotationUIDs) {
|
||||
addedSelectedAnnotationUIDs.forEach(annotationUID =>
|
||||
MeasurementService.setMeasurementSelected(annotationUID, true)
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to select and unselect measurements:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When csTools fires a removed event, remove the same measurement
|
||||
@ -157,13 +182,17 @@ const connectToolsToMeasurementService = (
|
||||
|
||||
// on display sets added, check if there are any measurements in measurement service that need to be
|
||||
// put into cornerstone tools
|
||||
const addedEvt = csToolsEvents.ANNOTATION_ADDED;
|
||||
const completedEvt = csToolsEvents.ANNOTATION_COMPLETED;
|
||||
const updatedEvt = csToolsEvents.ANNOTATION_MODIFIED;
|
||||
const removedEvt = csToolsEvents.ANNOTATION_REMOVED;
|
||||
const selectionEvt = csToolsEvents.ANNOTATION_SELECTION_CHANGE;
|
||||
|
||||
eventTarget.addEventListener(addedEvt, addMeasurement);
|
||||
eventTarget.addEventListener(completedEvt, addMeasurement);
|
||||
eventTarget.addEventListener(updatedEvt, updateMeasurement);
|
||||
eventTarget.addEventListener(removedEvt, removeMeasurement);
|
||||
eventTarget.addEventListener(selectionEvt, selectMeasurement);
|
||||
|
||||
return csTools3DVer1MeasurementSource;
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
const targets = Object.keys(cachedStats);
|
||||
|
||||
if (!targets.length) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const annotations = [];
|
||||
|
||||
@ -85,7 +85,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
const targets = Object.keys(cachedStats);
|
||||
|
||||
if (!targets.length) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const annotations = [];
|
||||
|
||||
@ -91,7 +91,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
const targets = Object.keys(cachedStats);
|
||||
|
||||
if (!targets.length) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const annotations = [];
|
||||
|
||||
@ -255,14 +255,13 @@ function _getMappedMeasurements(MeasurementService) {
|
||||
}
|
||||
|
||||
function _mapMeasurementToDisplay(measurement, index, types) {
|
||||
const { displayText, uid, label, type } = measurement;
|
||||
const { displayText, uid, label, type, selected } = measurement;
|
||||
|
||||
return {
|
||||
uid,
|
||||
label: label || '(empty)',
|
||||
measurementType: type,
|
||||
displayText: displayText || [],
|
||||
// TODO: handle one layer down
|
||||
isActive: false, // activeMeasurementItem === i + 1,
|
||||
isActive: selected,
|
||||
};
|
||||
}
|
||||
|
||||
@ -323,7 +323,7 @@ function _mapMeasurementToDisplay(measurement, types, DisplaySetService) {
|
||||
label: measurement.label || '(empty)',
|
||||
measurementType: measurement.type,
|
||||
displayText: displayText || [],
|
||||
isActive: false, // activeMeasurementItem === i + 1,
|
||||
isActive: measurement.selected,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@ import pubSubServiceInterface from '../_shared/pubSubServiceInterface';
|
||||
* @property {number} area -
|
||||
* @property {Array} points -
|
||||
* @property {MeasurementSource} source -
|
||||
* @property {boolean} selected -
|
||||
*/
|
||||
|
||||
/* Measurement schema keys for object validation. */
|
||||
@ -56,6 +57,7 @@ const MEASUREMENT_SCHEMA_KEYS = [
|
||||
'shortestDiameter',
|
||||
'longestDiameter',
|
||||
'cachedStats',
|
||||
'selected',
|
||||
];
|
||||
|
||||
const EVENTS = {
|
||||
@ -184,6 +186,20 @@ class MeasurementService {
|
||||
return measurement;
|
||||
}
|
||||
|
||||
setMeasurementSelected(measurementUID, selected) {
|
||||
const measurement = this.getMeasurement(measurementUID);
|
||||
if (!measurement) {
|
||||
return;
|
||||
}
|
||||
|
||||
measurement.selected = selected;
|
||||
|
||||
this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, {
|
||||
source: measurement.source,
|
||||
measurement,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new source.
|
||||
*
|
||||
@ -218,8 +234,17 @@ class MeasurementService {
|
||||
version,
|
||||
};
|
||||
|
||||
source.annotationToMeasurement = (annotationType, annotation) => {
|
||||
return this.annotationToMeasurement(source, annotationType, annotation);
|
||||
source.annotationToMeasurement = (
|
||||
annotationType,
|
||||
annotation,
|
||||
isUpdate = false
|
||||
) => {
|
||||
return this.annotationToMeasurement(
|
||||
source,
|
||||
annotationType,
|
||||
annotation,
|
||||
isUpdate
|
||||
);
|
||||
};
|
||||
|
||||
source.remove = (measurementUID, eventDetails) => {
|
||||
@ -467,9 +492,15 @@ class MeasurementService {
|
||||
* @param {MeasurementSource} source The measurement source instance
|
||||
* @param {string} annotationType The source annotationType
|
||||
* @param {EventDetail} sourceAnnotationDetail for the annotation event
|
||||
* @param {boolean} isUpdate is this an update or an add/completed instead?
|
||||
* @return {string} A measurement uid
|
||||
*/
|
||||
annotationToMeasurement(source, annotationType, sourceAnnotationDetail) {
|
||||
annotationToMeasurement(
|
||||
source,
|
||||
annotationType,
|
||||
sourceAnnotationDetail,
|
||||
isUpdate = false
|
||||
) {
|
||||
if (!this._isValidSource(source)) {
|
||||
throw new Error('Invalid source.');
|
||||
}
|
||||
@ -524,19 +555,27 @@ class MeasurementService {
|
||||
};
|
||||
|
||||
if (this.measurements[internalUID]) {
|
||||
// TODO: Ultimately, each annotation should have a selected flag right from the soure.
|
||||
// For now, it is just added in OHIF here and in setMeasurementSelected.
|
||||
newMeasurement.selected = this.measurements[internalUID].selected;
|
||||
this.measurements[internalUID] = newMeasurement;
|
||||
this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, {
|
||||
source,
|
||||
measurement: newMeasurement,
|
||||
notYetUpdatedAtSource: false,
|
||||
});
|
||||
if (isUpdate) {
|
||||
this._broadcastEvent(this.EVENTS.MEASUREMENT_UPDATED, {
|
||||
source,
|
||||
measurement: newMeasurement,
|
||||
notYetUpdatedAtSource: false,
|
||||
});
|
||||
} else {
|
||||
log.info('Measurement added.', newMeasurement);
|
||||
this.measurements[internalUID] = newMeasurement;
|
||||
this._broadcastEvent(this.EVENTS.MEASUREMENT_ADDED, {
|
||||
source,
|
||||
measurement: newMeasurement,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
log.info('Measurement added.', newMeasurement);
|
||||
log.info('Measurement started.', newMeasurement);
|
||||
this.measurements[internalUID] = newMeasurement;
|
||||
this._broadcastEvent(this.EVENTS.MEASUREMENT_ADDED, {
|
||||
source,
|
||||
measurement: newMeasurement,
|
||||
});
|
||||
}
|
||||
|
||||
return newMeasurement.uid;
|
||||
@ -588,16 +627,10 @@ class MeasurementService {
|
||||
}
|
||||
this._addJumpToMeasurement(viewportIndex, measurementUID);
|
||||
|
||||
const eventName = this.EVENTS.JUMP_TO_MEASUREMENT;
|
||||
|
||||
const hasListeners = Object.keys(this.listeners).length > 0;
|
||||
const hasCallbacks = Array.isArray(this.listeners[eventName]);
|
||||
|
||||
if (hasListeners && hasCallbacks) {
|
||||
this.listeners[eventName].forEach(listener => {
|
||||
listener.callback({ viewportIndex, measurement });
|
||||
});
|
||||
}
|
||||
this._broadcastEvent(this.EVENTS.JUMP_TO_MEASUREMENT, {
|
||||
viewportIndex,
|
||||
measurement,
|
||||
});
|
||||
}
|
||||
|
||||
getJumpToMeasurement(viewportIndex) {
|
||||
|
||||
@ -364,8 +364,9 @@ describe('MeasurementService.js', () => {
|
||||
() => (addCallbackWasCalled = true)
|
||||
);
|
||||
|
||||
/* Add new measurement */
|
||||
source.annotationToMeasurement(annotationType, measurement);
|
||||
/* Add new measurement - two calls needed for the start and the other for the completed*/
|
||||
const uid = source.annotationToMeasurement(annotationType, measurement);
|
||||
source.annotationToMeasurement(annotationType, { uid, ...measurement });
|
||||
|
||||
expect(addCallbackWasCalled).toBe(true);
|
||||
});
|
||||
@ -392,7 +393,11 @@ describe('MeasurementService.js', () => {
|
||||
const uid = source.annotationToMeasurement(annotationType, measurement);
|
||||
|
||||
/* Update measurement */
|
||||
source.annotationToMeasurement(annotationType, { uid, ...measurement });
|
||||
source.annotationToMeasurement(
|
||||
annotationType,
|
||||
{ uid, ...measurement },
|
||||
true
|
||||
);
|
||||
|
||||
expect(updateCallbackWasCalled).toBe(true);
|
||||
});
|
||||
@ -418,8 +423,9 @@ describe('MeasurementService.js', () => {
|
||||
/* Unsubscribe */
|
||||
unsubscribe();
|
||||
|
||||
/* Create measurement */
|
||||
source.annotationToMeasurement(annotationType, measurement);
|
||||
/* Create measurement - two calls needed one to start and one to complete */
|
||||
const uid = source.annotationToMeasurement(annotationType, measurement);
|
||||
source.annotationToMeasurement(annotationType, { uid, ...measurement });
|
||||
|
||||
expect(updateCallbackWasCalled).toBe(false);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user