Skip to content

Commit

Permalink
Merge pull request #549 from agirault/abstract-widget-events
Browse files Browse the repository at this point in the history
Abstract widget events handling
  • Loading branch information
jourdain authored Jan 31, 2018
2 parents e1b5a7d + 93be5c3 commit 157f344
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 214 deletions.
6 changes: 4 additions & 2 deletions Sources/Interaction/Widgets/AbstractWidget/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ It defines the representation of the widget

## listenEvents()

Virtual method, needs to be overrides in derived class.
It defined which methods will be invoked for each event (see vtkHandleWidget)
Attaches interactor events to callbacks. Events are defined in
vtkRenderWindowInteractor.handledEvents, and callbacks need to be formatted
as handle${eventToHandle} and implemented in derived classes.
example: handleMouseMove, handleLeftButtonPress...

## render()

Expand Down
53 changes: 42 additions & 11 deletions Sources/Interaction/Widgets/AbstractWidget/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import macro from 'vtk.js/Sources/macro';
import vtkInteractorObserver from 'vtk.js/Sources/Rendering/Core/InteractorObserver';
import vtkRenderWindowInteractor from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor';

const { vtkErrorMacro } = macro;

Expand All @@ -14,8 +15,42 @@ function vtkAbstractWidget(publicAPI, model) {
// Virtual method
publicAPI.createDefaultRepresentation = () => {};

// Virtual method
publicAPI.listenEvents = () => {};
publicAPI.listenEvents = () => {
if (!model.interactor) {
vtkErrorMacro('The interactor must be set before listening events');
return;
}

// Remove current events
while (model.unsubscribes.length) {
model.unsubscribes.pop().unsubscribe();
}

// Check what events we can handle and register callbacks
vtkRenderWindowInteractor.handledEvents.forEach((eventName) => {
if (publicAPI[`handle${eventName}`]) {
model.unsubscribes.push(
model.interactor[`on${eventName}`](
publicAPI[`handle${eventName}`],
model.priority
)
);
}
});
};

publicAPI.setInteractor = (i) => {
if (i === model.interactor) {
return;
}
model.interactor = i;

if (i && model.enabled) {
publicAPI.listenEvents();
}

publicAPI.modified();
};

publicAPI.render = () => {
if (!model.parent && model.interactor) {
Expand All @@ -24,11 +59,11 @@ function vtkAbstractWidget(publicAPI, model) {
};

publicAPI.setEnable = (enable) => {
if (enable === model.enabled) {
return;
}

if (enable) {
if (model.enabled) {
// widget already enabled
return;
}
if (!model.interactor) {
vtkErrorMacro(
'The interactor must be set prior to enabling the widget'
Expand Down Expand Up @@ -61,13 +96,9 @@ function vtkAbstractWidget(publicAPI, model) {
model.widgetRep.buildRepresentation();
model.currentRenderer.addViewProp(model.widgetRep);
} else {
if (!model.enabled) {
// already
return;
}
model.enabled = 0;

// Don't listen events
// Don't listen to events
while (model.unsubscribes.length) {
model.unsubscribes.pop().unsubscribe();
}
Expand Down
9 changes: 1 addition & 8 deletions Sources/Interaction/Widgets/HandleWidget/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ General widget for moving handles (translate and scale)

## Caught events

List of event catch by the widget :
List of events caught by the widget :
- MouseMove
- LeftButtonPress
- LeftButtonRelease
Expand All @@ -21,13 +21,6 @@ List of event catch by the widget :

Create a vtkSphereHandleRepresentation as a default representation.

## listenEvents()

For each events, define a method called when event is caught :
- onMouseMove then call handleMouseMove
- onLeftButtonPress then call handleLeftButtonPress
- ...

## allowHandleResize (set/get bool)

Define if the widget representation can be resized (default is true)
127 changes: 36 additions & 91 deletions Sources/Interaction/Widgets/HandleWidget/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,14 @@ import vtkSphereHandleRepresentation from 'vtk.js/Sources/Interaction/Widgets/Sp
import vtkHandleRepresentation from 'vtk.js/Sources/Interaction/Widgets/HandleRepresentation';
import Constants from 'vtk.js/Sources/Interaction/Widgets/HandleWidget/Constants';

const { VOID, EVENT_ABORT } = macro;
const { InteractionState } = vtkHandleRepresentation;
const { WidgetState } = Constants;
const { vtkErrorMacro } = macro;

// ----------------------------------------------------------------------------
// vtkHandleWidget methods
// ----------------------------------------------------------------------------

const events = [
'MouseMove',
'LeftButtonPress',
'LeftButtonRelease',
'MiddleButtonPress',
'MiddleButtonRelease',
'RightButtonPress',
'RightButtonRelease',
];

function vtkHandleWidget(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkHandleWidget');
Expand Down Expand Up @@ -53,69 +43,19 @@ function vtkHandleWidget(publicAPI, model) {
}
};

// Implemented method
publicAPI.listenEvents = () => {
if (!model.interactor) {
vtkErrorMacro('The interactor must be set before listening events');
return;
}
events.forEach((eventName) => {
model.unsubscribes.push(
model.interactor[`on${eventName}`](() => {
if (publicAPI[`handle${eventName}`]) {
publicAPI[`handle${eventName}`]();
}
})
);
});
};
publicAPI.handleMouseMove = () => publicAPI.moveAction();

publicAPI.setInteractor = (i) => {
if (i === model.interactor) {
return;
}
publicAPI.handleLeftButtonPress = () => publicAPI.selectAction();

// if we already have an Interactor then stop observing it
if (model.interactor) {
while (model.unsubscribes.length) {
model.unsubscribes.pop().unsubscribe();
}
}
publicAPI.handleLeftButtonRelease = () => publicAPI.endSelectAction();

model.interactor = i;
publicAPI.handleMiddleButtonPress = () => publicAPI.translateAction();

if (i) {
publicAPI.listenEvents();
}
};
publicAPI.handleMiddleButtonRelease = () => publicAPI.endSelectAction();

publicAPI.handleMouseMove = () => {
publicAPI.moveAction();
};
publicAPI.handleRightButtonPress = () => publicAPI.scaleAction();

publicAPI.handleLeftButtonPress = () => {
publicAPI.selectAction();
};

publicAPI.handleLeftButtonRelease = () => {
publicAPI.endSelectAction();
};

publicAPI.handleMiddleButtonPress = () => {
publicAPI.translateAction();
};

publicAPI.handleMiddleButtonRelease = () => {
publicAPI.endSelectAction();
};

publicAPI.handleRightButtonPress = () => {
publicAPI.scaleAction();
};

publicAPI.handleRightButtonRelease = () => {
publicAPI.endSelectAction();
};
publicAPI.handleRightButtonRelease = () => publicAPI.endSelectAction();

publicAPI.selectAction = () => {
const pos = model.interactor.getEventPosition(
Expand All @@ -130,13 +70,13 @@ function vtkHandleWidget(publicAPI, model) {
];
model.widgetRep.computeInteractionState(position);
if (model.widgetRep.getInteractionState() === InteractionState.OUTSIDE) {
return;
return VOID;
}

model.widgetRep.startComplexWidgetInteraction(position);
model.widgetState = WidgetState.ACTIVE;
model.widgetRep.setInteractionState(InteractionState.SELECTING);
genericAction();
return EVENT_ABORT;
};

publicAPI.translateAction = () => {
Expand All @@ -152,43 +92,47 @@ function vtkHandleWidget(publicAPI, model) {
];
model.widgetRep.startComplexWidgetInteraction(position);
if (model.widgetRep.getInteractionState() === InteractionState.OUTSIDE) {
return;
return VOID;
}
model.widgetState = WidgetState.ACTIVE;
model.widgetRep.setInteractionState(InteractionState.TRANSLATING);
genericAction();
return EVENT_ABORT;
};

publicAPI.scaleAction = () => {
if (model.allowHandleResize) {
const pos = model.interactor.getEventPosition(
model.interactor.getPointerIndex()
);
const boundingContainer = model.interactor
.getCanvas()
.getBoundingClientRect();
const position = [
pos.x - boundingContainer.left,
pos.y + boundingContainer.top,
];
model.widgetRep.startComplexWidgetInteraction(position);
if (model.widgetRep.getInteractionState() === InteractionState.OUTSIDE) {
return;
}
model.widgetState = WidgetState.ACTIVE;
model.widgetRep.setInteractionState(InteractionState.SCALING);
genericAction();
if (!model.allowHandleResize) {
return VOID;
}
const pos = model.interactor.getEventPosition(
model.interactor.getPointerIndex()
);
const boundingContainer = model.interactor
.getCanvas()
.getBoundingClientRect();
const position = [
pos.x - boundingContainer.left,
pos.y + boundingContainer.top,
];
model.widgetRep.startComplexWidgetInteraction(position);
if (model.widgetRep.getInteractionState() === InteractionState.OUTSIDE) {
return VOID;
}
model.widgetState = WidgetState.ACTIVE;
model.widgetRep.setInteractionState(InteractionState.SCALING);
genericAction();
return EVENT_ABORT;
};

publicAPI.endSelectAction = () => {
if (model.widgetState !== WidgetState.ACTIVE) {
return;
return VOID;
}
model.widgetState = WidgetState.START;
model.widgetRep.highlight(0);
publicAPI.invokeEndInteractionEvent();
publicAPI.render();
return EVENT_ABORT;
};

publicAPI.moveAction = () => {
Expand All @@ -212,12 +156,13 @@ function vtkHandleWidget(publicAPI, model) {
) {
publicAPI.render();
}
return;
return VOID;
}

model.widgetRep.complexWidgetInteraction(position);
publicAPI.invokeInteractionEvent();
publicAPI.render();
return EVENT_ABORT;
};
}

Expand Down
49 changes: 0 additions & 49 deletions Sources/Interaction/Widgets/ImageCroppingRegionsWidget/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@ import Constants from 'vtk.js/Sources/Interaction/Widgets/ImageCroppingRegionsWi
const { vtkErrorMacro, VOID, EVENT_ABORT } = macro;
const { WidgetState, CropWidgetEvents, Orientation } = Constants;

const events = [
'MouseMove',
'LeftButtonPress',
'LeftButtonRelease',
'MiddleButtonPress',
'MiddleButtonRelease',
'RightButtonPress',
'RightButtonRelease',
];

// ----------------------------------------------------------------------------
// vtkImageCroppingRegionsWidget methods
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -64,45 +54,6 @@ function vtkImageCroppingRegionsWidget(publicAPI, model) {
}
};

// Implemented method
publicAPI.listenEvents = () => {
if (!model.interactor) {
vtkErrorMacro('The interactor must be set before listening events');
return;
}
events.forEach((eventName) => {
model.unsubscribes.push(
model.interactor[`on${eventName}`](() => {
if (publicAPI[`handle${eventName}`]) {
return publicAPI[`handle${eventName}`]();
}
return true;
}, model.priority)
);
});
};

publicAPI.setInteractor = (i) => {
if (i === model.interactor) {
return;
}

// if we already have an Interactor then stop observing it
if (model.interactor) {
while (model.unsubscribes.length) {
model.unsubscribes.pop().unsubscribe();
}
}

model.interactor = i;

if (i) {
publicAPI.listenEvents();
}

publicAPI.modified();
};

publicAPI.setVolumeMapper = (volumeMapper) => {
if (volumeMapper !== model.volumeMapper) {
model.volumeMapper = volumeMapper;
Expand Down
Loading

0 comments on commit 157f344

Please sign in to comment.