-
Notifications
You must be signed in to change notification settings - Fork 326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(Touch): added touch events to tools #247
feat(Touch): added touch events to tools #247
Conversation
✅ Deploy Preview for cornerstone-3d-docs ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
d3827d4
to
f2fe364
Compare
@sedghi let me know what you think |
f2fe364
to
0d58ee2
Compare
This looks great at first sight. So are you plannig to uncommente the touch event listeners in annotation tools later? Like here https://github.com/cornerstonejs/cornerstone3D-beta/blob/main/packages/tools/src/tools/annotation/LengthTool.ts#L490 Also, are you saying we don't need the hammerJS anymore? |
I think either hammerjs or contactjs will be needed to trigger the pinch/pan functionality So I had some questions regarding hammerjs usage in the cornerstoneTools v2. I noticed that hammerjs has a press and tap functionality, but cornerstone touch eventlistener seem to internally implement the press/tap. Was there a reason for this? cornerstonejs/cornerstoneTools#120 Last question in cornerstoneTools v2, there were some touch specific tool copies (pan vs panmultitouch) would it make more sense to have the pan tool have configuration which enables multitooltouch instead of these being two separate tools? |
Hammer.js would add MULTI_TOUCH_PAN, PINCH, and ROTATE, PRESS (longer and tap) |
Example to get shown need to be added here |
In case of changing serve, we need to update the docs |
Is this ready to go? |
Not yet, am in the process of adding the multitouch and gestures. I think we can avoid hammerjs dependency as well |
0d58ee2
to
b14c070
Compare
@sedghi this is almost ready for your review. This is becoming a larger change than I initially intended. Highlights
TODO:
|
9bac6d5
to
a49735b
Compare
a49735b
to
7580eeb
Compare
7580eeb
to
7219316
Compare
/** | ||
* The normalized mouse event detail | ||
*/ | ||
type NormalizedTouchEventDetail = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't have anything different from the normalized mouse event detail - please re-use the existing type (you can rename it if you want, but only have one instance of it)
/** | ||
* EventDetail for touchstart event | ||
*/ | ||
type TouchStartEventDetail = NormalizedTouchEventDetail & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please start with the MouseStartEventDetail, and then add specific behaviour that identifies this as a touch event. That allows leaving most of the tools classes alone, and only tweaking them if needed. There should/must be an identifier that identifies this as a touch event, and probably something that identifies what "drag" type to listen to.
*/ | ||
type TouchStartEventDetail = NormalizedTouchEventDetail & { | ||
/** The starting points of the touch event. */ | ||
startPoints: ITouchPoints; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let the values that can be inheritted from MouseStartEventDetail be inheritted, and only add the extras as required.
@@ -101,6 +103,10 @@ class MagnifyTool extends BaseTool { | |||
return true; | |||
}; | |||
|
|||
preTouchStartCallback = (evt: EventTypes.TouchStartActivateEventType) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You won't need many, if any of this type of change if the preMouseDownCallback just takes a mouse down activate event type, and can check if it is a touch event sub-type.
64e932c
to
78287da
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a pretty thorough review for each file. I want to be mindful that this is a very large PR and would prefer it to go in before other tool related PRs if possible @sedghi. Otherwise subsequent changes will need to rebased and force push might sneak diffs.
These were the main changes I found, but many of them are cosmetic which might be better handled in another (smaller) PR.
evt: EventTypes.MouseDownActivateEventType, | ||
interactionType: string | ||
evt: EventTypes.InteractionEventType, | ||
interactionType = 'mouse' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is used in this function. Minor nit that might be better to do in a separate PR.
@@ -469,7 +469,7 @@ class CrosshairsTool extends AnnotationTool { | |||
} | |||
|
|||
handleSelectedCallback = ( | |||
evt: EventTypes.MouseDownEventType, | |||
evt: EventTypes.InteractionEventType, | |||
annotation: Annotation, | |||
handle: ToolHandle, | |||
interactionType = 'mouse' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
doubleClickCallback = (evt: EventTypes.MouseUpEventType) => { | ||
touchTapCallback = (evt: EventTypes.TouchTapEventType) => { | ||
if (evt.detail.taps == 2) { | ||
this.doubleClickCallback(evt as unknown as EventTypes.MouseUpEventType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can pass evt without as unknown as EventTypes...
with the InteractionEventType
change we made
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I think we need to use the specific TouchTapEventDetail here because we don't have the "taps" parameter from InteractionEventType. So "TouchTapEventDetail" is inherited from the interaction.
type TouchTapEventDetail = NormalizedInteractionEventDetail &
TouchCustomEventDetail & {
currentPointsList: ITouchPoints[];
currentPoints: ITouchPoints;
taps: number; <----
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we discussed yesterday, this is a special case where we need to use the taps parameter and we don't have it inherited from the InteractionType. So, make sense to use the TouchTapEventDetail at this specific point.
@@ -51,7 +51,7 @@ class DragProbeTool extends ProbeTool { | |||
} | |||
|
|||
postMouseDownCallback = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a postTouchDownCallback
exposed to have touch events dispatched to this tool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"postTouchDownCallback" or "postTouchStartCallback"?
I believe we're using the "postTouchStartCallback", see below:
packages/tools/src/eventDispatchers/touchEventHandler/touchstart.ts
if (activeTool && typeof activeTool.postTouchStartCallback === 'function') {
const consumedEvent = activeTool.postTouchStartCallback(evt);
if (consumedEvent) {
// If the tool claims it consumed the event, prevent further checks.
return;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be postTouchStart callback you are correct. Rather than copying the mouse function you can just call the postMouseDownCallback function inside of postTouchStart callback
@@ -172,7 +172,8 @@ abstract class AnnotationTool extends AnnotationDisplayTool { | |||
element: HTMLDivElement, | |||
annotation: Annotation, | |||
canvasCoords: Types.Point2, | |||
proximity: number | |||
proximity: number, | |||
interactionType = 'mouse' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be removed
// element.removeEventListener(Events.TOUCH_END, this._mouseUpCallback) | ||
// element.removeEventListener(Events.TOUCH_DRAG, this._mouseDragCallback) | ||
// element.removeEventListener(Events.TOUCH_END, this._endCallback) | ||
// element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrongly commented out same as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also removeEventListener to the Events.TOUCH_TAP event
// element.addEventListener(Events.TOUCH_END, this._mouseUpCallback) | ||
// element.addEventListener(Events.TOUCH_DRAG, this._mouseDragCallback) | ||
// element.addEventListener(Events.TOUCH_END, this._endCallback) | ||
// element.addEventListener(Events.TOUCH_DRAG, this._dragCallback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be enabled and include
element.addEventListener(Events.TOUCH_TAP, this._endCallback)
// element.removeEventListener(Events.TOUCH_END, this._mouseUpCallback) | ||
// element.removeEventListener(Events.TOUCH_DRAG, this._mouseDragCallback) | ||
// element.removeEventListener(Events.TOUCH_END, this._endCallback) | ||
// element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
This should be enabled and include
element. removeEventListener(Events.TOUCH_TAP, this._endCallback)
// element.addEventListener(Events.TOUCH_END, this._mouseUpCallback) | ||
// element.addEventListener(Events.TOUCH_DRAG, this._mouseDragCallback) | ||
// element.addEventListener(Events.TOUCH_END, this._endCallback) | ||
// element.addEventListener(Events.TOUCH_DRAG, this._dragCallback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as other segmentation tools.
// element.removeEventListener(Events.TOUCH_END, this._mouseUpCallback) | ||
// element.removeEventListener(Events.TOUCH_DRAG, this._mouseDragCallback) | ||
// element.removeEventListener(Events.TOUCH_END, this._endCallback) | ||
// element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as other segmentation tools
@sedghi lmk if there are any outstanding changes required aside from the above... thanks again for taking the time to review this large diff which pokes other existing files. |
@Ouwen probably yes, but I think I would like to do one last review. Just let me know when it is ready for it |
} | ||
}; | ||
|
||
doubleClickCallback = (evt: EventTypes.InteractionEventType): void => { | ||
doubleClickCallback = (evt: EventTypes.TouchTapEventType): void => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should stay InteractionEventType since taps are not used
@@ -405,11 +405,11 @@ class ArrowAnnotateTool extends AnnotationTool { | |||
|
|||
touchTapCallback = (evt: EventTypes.TouchTapEventType) => { | |||
if (evt.detail.taps == 2) { | |||
this.doubleClickCallback(evt as unknown as EventTypes.MouseUpEventType); | |||
this.doubleClickCallback(evt as EventTypes.TouchTapEventType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should just be evt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be evt since touchTapCallback = (evt: EventTypes.TouchTapEventType) => {
@@ -321,8 +321,8 @@ class RectangleScissorsTool extends BaseTool { | |||
element.removeEventListener(Events.MOUSE_CLICK, this._endCallback); | |||
element.removeEventListener(Events.TOUCH_TAP, this._endCallback); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's move the touch_tap back down to after line 325 to match mouse formatting
@sedghi, I went through another pass at it, I think it is ready for review |
toolGroup.setToolActive(WindowLevelTool.toolName, { | ||
bindings: [ | ||
{ | ||
mouseButton: MouseBindings.Primary, // special condition for one finger touch | ||
}, | ||
], | ||
}); | ||
``` | ||
|
||
The `MouseBindings.Primary` is a special binding type which will | ||
automatically bind single finger touch. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand, why the API here is different, is this supposed to do a mouse binding or touch bindings? If it is touch, I expected the following for consistensy
toolGroup.setToolActive(WindowLevelTool.toolName, {
bindings: [{ numTouchPoints: 1 }],
});
toolGroup.setToolActive(WindowLevelTool.toolName, { | ||
bindings: [ | ||
{ | ||
mouseButton: MouseBindings.Primary, // Left Click | ||
}, | ||
], | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember you said this
Yeah numTouchPoints = 1 is a special case where we also treat it as a "Primary Mouse" event. This way existing modes which just bind a primary mouse get the free touch win without additional explicit config.
But don't think we should treat the numPoint ===1 special, why not for the others? why just 1 ?
packages/tools/src/eventDispatchers/mouseEventHandlers/mouseMove.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The work done on this PR is truly exceptional and greatly appreciated. Kudos to the Gradient Health team as always 🎉 💯
Adds the following cornerstone events:
Currently only uses native browser events (touchstart, touchmove, touchend)