Mercurial > hg > orthanc-stone
changeset 1561:cf652990abb1
tunable mouse actions
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 20 Aug 2020 17:44:35 +0200 |
parents | b4ccd4963d37 |
children | 2a4a6b967053 |
files | Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/index.html Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/StoneEnumerations.h OrthancStone/Sources/Viewport/DefaultViewportInteractor.cpp OrthancStone/Sources/Viewport/DefaultViewportInteractor.h |
diffstat | 6 files changed, 275 insertions(+), 46 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebApplication/app.js Thu Aug 20 13:57:52 2020 +0200 +++ b/Applications/StoneWebViewer/WebApplication/app.js Thu Aug 20 17:44:35 2020 +0200 @@ -104,6 +104,7 @@ leftMode: 'grid', // Can be 'small', 'grid' or 'full' leftVisible: true, viewportLayoutButtonsVisible: false, + mouseActionsVisible: false, activeViewport: 0, showInfo: true, showReferenceLines: true, @@ -193,7 +194,7 @@ } }, methods: { - FitContent() { + FitContent: function() { // This function can be used even if WebAssembly is not initialized yet if (typeof stone._AllViewportsUpdateSize !== 'undefined') { this.$nextTick(function () { @@ -202,7 +203,7 @@ } }, - GetActiveSeries() { + GetActiveSeries: function() { var s = []; if ('tags' in this.viewport1Series) @@ -220,7 +221,7 @@ return s; }, - GetActiveCanvas() { + GetActiveCanvas: function() { if (this.activeViewport == 1) { return 'canvas1'; } @@ -445,48 +446,57 @@ } }, - SetWindowing(center, width) { + SetWindowing: function(center, width) { var canvas = this.GetActiveCanvas(); if (canvas != '') { stone.SetWindowing(canvas, center, width); } }, - SetDefaultWindowing() { + SetDefaultWindowing: function() { var canvas = this.GetActiveCanvas(); if (canvas != '') { stone.SetDefaultWindowing(canvas); } }, - InvertContrast() { + InvertContrast: function() { var canvas = this.GetActiveCanvas(); if (canvas != '') { stone.InvertContrast(canvas); } }, - FlipX() { + FlipX: function() { var canvas = this.GetActiveCanvas(); if (canvas != '') { stone.FlipX(canvas); } }, - FlipY() { + FlipY: function() { var canvas = this.GetActiveCanvas(); if (canvas != '') { stone.FlipY(canvas); } }, - ApplyPreferences() { + ApplyPreferences: function() { this.modalPreferences = false; if ((stone.IsSoftwareRendering() != 0) != this.settingSoftwareRendering) { document.location.reload(); } - } + }, + + HideAllTooltips: function() { + $('[data-toggle="tooltip"]').tooltip('hide'); + }, + + SetMouseButtonActions: function(left, middle, right) { + this.mouseActionsVisible = false; + stone.SetMouseButtonActions(left, middle, right); + } }, mounted: function() { @@ -609,7 +619,11 @@ $(document).ready(function() { // Enable support for tooltips in Bootstrap - //$('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').tooltip({ + placement: 'bottom', + container: 'body', + trigger: 'hover' + }); //app.modalWarning = true;
--- a/Applications/StoneWebViewer/WebApplication/index.html Thu Aug 20 13:57:52 2020 +0200 +++ b/Applications/StoneWebViewer/WebApplication/index.html Thu Aug 20 17:44:35 2020 +0200 @@ -255,7 +255,8 @@ <div class="tbGroup__toggl"> <button class="wvButton" v-bind:class="{ 'wvButton--underline' : !viewportLayoutButtonsVisible }" - @click="viewportLayoutButtonsVisible = !viewportLayoutButtonsVisible"> + data-toggle="tooltip" data-title="Change layout" + @click="viewportLayoutButtonsVisible = !viewportLayoutButtonsVisible;HideAllTooltips()"> <i class="fa fa-th"></i> </button> </div> @@ -285,6 +286,46 @@ </div> </div> + + <div class="ng-scope inline-object"> + <div class="tbGroup"> + <div class="tbGroup__toggl"> + <button class="wvButton" + v-bind:class="{ 'wvButton--underline' : !mouseActionsVisible }" + data-toggle="tooltip" data-title="Mouse actions" + @click="mouseActionsVisible = !mouseActionsVisible;HideAllTooltips()"> + <i class="fa fa-mouse-pointer"></i> + </button> + </div> + + <div class="tbGroup__buttons--bottom" v-show="mouseActionsVisible"> + <div class="inline-object"> + <button class="wvButton" + data-toggle="tooltip" data-title="Combined tool" + @click="SetMouseButtonActions(stone.MouseAction.GRAYSCALE_WINDOWING, stone.MouseAction.PAN, stone.MouseAction.ZOOM)"> + <i class="far fa-hand-point-up"></i> + </button> + </div> + <div class="inline-object"> + <button class="wvButton" + data-toggle="tooltip" data-title="Zoom" + @click="SetMouseButtonActions(stone.MouseAction.ZOOM, stone.MouseAction.ZOOM, stone.MouseAction.ZOOM)"> + <i class="fas fa-search"></i> + </button> + </div> + <div class="inline-object"> + <button class="wvButton" + data-toggle="tooltip" data-title="Pan" + @click="SetMouseButtonActions(stone.MouseAction.PAN, stone.MouseAction.PAN, stone.MouseAction.PAN)"> + <i class="fas fa-arrows-alt"></i> + </button> + </div> + </div> + </div> + </div> + + + <!--div class="ng-scope inline-object"> <button class="wvButton--underline text-center active"> <i class="fa fa-hand-pointer-o"></i> @@ -305,31 +346,39 @@ <div class="ng-scope inline-object"> <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Invert contrast" v-on:click="InvertContrast()"> <i class="fa fa-adjust"></i> </button> </div> <div class="ng-scope inline-object"> - <button class="wvButton--underline text-center" id="windowing-popover"> + <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Change windowing" + id="windowing-popover"> <i class="fa fa-sun"></i> </button> </div> <div class="ng-scope inline-object"> - <button class="wvButton--underline text-center" v-on:click="FlipX()"> + <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Flip horizontally" + v-on:click="FlipX()"> <i class="fas fa-exchange-alt"></i> </button> </div> <div class="ng-scope inline-object"> - <button class="wvButton--underline text-center" v-on:click="FlipY()"> + <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Flip vertically" + v-on:click="FlipY()"> <i class="fas fa-exchange-alt fa-rotate-90"></i> </button> </div> <div class="ng-scope inline-object"> <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Show image information" v-bind:class="{ 'active' : showInfo }" v-on:click="showInfo = !showInfo"> <i class="fa fa-info-circle"></i> @@ -338,6 +387,7 @@ <div class="ng-scope inline-object"> <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="Reference lines" v-bind:class="{ 'active' : showReferenceLines }" v-on:click="showReferenceLines = !showReferenceLines"> <i class="fa fa-bars"></i> @@ -346,6 +396,7 @@ <div class="ng-scope inline-object"> <button class="wvButton--underline text-center" + data-toggle="tooltip" data-title="User preferences" v-on:click="modalPreferences = true"> <i class="fa fa-user"></i> </button>
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Thu Aug 20 13:57:52 2020 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Thu Aug 20 17:44:35 2020 +0200 @@ -79,6 +79,7 @@ #include <Toolbox/DicomInstanceParameters.h> #include <Toolbox/GeometryToolbox.h> #include <Toolbox/SortedFrames.h> +#include <Viewport/DefaultViewportInteractor.h> #include <Viewport/WebAssemblyCairoViewport.h> #include <Viewport/WebGLViewport.h> @@ -99,10 +100,19 @@ enum EMSCRIPTEN_KEEPALIVE DisplayedFrameQuality { -DisplayedFrameQuality_None, - DisplayedFrameQuality_Low, - DisplayedFrameQuality_High - }; + DisplayedFrameQuality_None, + DisplayedFrameQuality_Low, + DisplayedFrameQuality_High + }; + + +enum EMSCRIPTEN_KEEPALIVE MouseAction +{ + MouseAction_GrayscaleWindowing, + MouseAction_Zoom, + MouseAction_Pan, + MouseAction_Rotate + }; @@ -1372,6 +1382,27 @@ } } + + void SaveCurrentWindowing() + { + // Save the current windowing (that could have been altered by + // GrayscaleWindowingSceneTracker), so that it can be reused + // by the next frames + + std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); + + OrthancStone::Scene2D& scene = lock->GetController().GetScene(); + + if (scene.HasLayer(LAYER_TEXTURE) && + scene.GetLayer(LAYER_TEXTURE).GetType() == OrthancStone::ISceneLayer::Type_FloatTexture) + { + OrthancStone::FloatTextureSceneLayer& layer = + dynamic_cast<OrthancStone::FloatTextureSceneLayer&>(scene.GetLayer(LAYER_TEXTURE)); + layer.GetWindowing(windowingCenter_, windowingWidth_); + } + } + + bool DisplayFrame(unsigned int& quality, size_t index) { @@ -1386,22 +1417,7 @@ FramesCache::Accessor accessor(*cache_, sopInstanceUid, frame); if (accessor.IsValid()) { - { - std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); - - OrthancStone::Scene2D& scene = lock->GetController().GetScene(); - - // Save the current windowing (that could have been altered by - // GrayscaleWindowingSceneTracker), so that it can be reused - // by the next frames - if (scene.HasLayer(LAYER_TEXTURE) && - scene.GetLayer(LAYER_TEXTURE).GetType() == OrthancStone::ISceneLayer::Type_FloatTexture) - { - OrthancStone::FloatTextureSceneLayer& layer = - dynamic_cast<OrthancStone::FloatTextureSceneLayer&>(scene.GetLayer(LAYER_TEXTURE)); - layer.GetWindowing(windowingCenter_, windowingWidth_); - } - } + SaveCurrentWindowing(); quality = accessor.GetQuality(); @@ -1842,12 +1858,14 @@ void FlipX() { flipX_ = !flipX_; + SaveCurrentWindowing(); UpdateCurrentTextureParameters(); } void FlipY() { flipY_ = !flipY_; + SaveCurrentWindowing(); UpdateCurrentTextureParameters(); } @@ -1873,6 +1891,21 @@ } } } + + void SetMouseButtonActions(OrthancStone::MouseAction leftAction, + OrthancStone::MouseAction middleAction, + OrthancStone::MouseAction rightAction) + { + std::unique_ptr<OrthancStone::DefaultViewportInteractor> interactor( + new OrthancStone::DefaultViewportInteractor); + + interactor->SetLeftButtonAction(leftAction); + interactor->SetMiddleButtonAction(middleAction); + interactor->SetRightButtonAction(rightAction); + + assert(viewport_ != NULL); + viewport_->AcquireInteractor(interactor.release()); + } }; @@ -1981,7 +2014,9 @@ static boost::shared_ptr<OrthancStone::WebAssemblyLoadersContext> context_; static std::string stringBuffer_; static bool softwareRendering_ = false; - +static OrthancStone::MouseAction leftButtonAction_ = OrthancStone::MouseAction_GrayscaleWindowing; +static OrthancStone::MouseAction middleButtonAction_ = OrthancStone::MouseAction_Pan; +static OrthancStone::MouseAction rightButtonAction_ = OrthancStone::MouseAction_Zoom; static void FormatTags(std::string& target, @@ -2027,6 +2062,7 @@ std::unique_ptr<OrthancStone::ILoadersContext::ILock> lock(context_->Lock()); boost::shared_ptr<ViewerViewport> viewport( ViewerViewport::Create(*lock, source_, canvas, cache_, softwareRendering_)); + viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_); viewport->AcquireObserver(new WebAssemblyObserver); allViewports_[canvas] = viewport; return viewport; @@ -2038,6 +2074,30 @@ } + +static OrthancStone::MouseAction ConvertMouseAction(int action) +{ + switch (action) + { + case MouseAction_GrayscaleWindowing: + return OrthancStone::MouseAction_GrayscaleWindowing; + + case MouseAction_Zoom: + return OrthancStone::MouseAction_Zoom; + + case MouseAction_Pan: + return OrthancStone::MouseAction_Pan; + + case MouseAction_Rotate: + return OrthancStone::MouseAction_Rotate; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } +} + + + extern "C" { int main(int argc, char const *argv[]) @@ -2377,4 +2437,25 @@ { return softwareRendering_; } + + + EMSCRIPTEN_KEEPALIVE + void SetMouseButtonActions(int leftAction, + int middleAction, + int rightAction) + { + try + { + leftButtonAction_ = ConvertMouseAction(leftAction); + middleButtonAction_ = ConvertMouseAction(middleAction); + rightButtonAction_ = ConvertMouseAction(rightAction); + + for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) + { + assert(it->second != NULL); + it->second->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_); + } + } + EXTERN_CATCH_EXCEPTIONS; + } }
--- a/OrthancStone/Sources/StoneEnumerations.h Thu Aug 20 13:57:52 2020 +0200 +++ b/OrthancStone/Sources/StoneEnumerations.h Thu Aug 20 17:44:35 2020 +0200 @@ -132,6 +132,14 @@ SliceAction_FastMinus }; + enum MouseAction + { + MouseAction_Pan, + MouseAction_Zoom, + MouseAction_Rotate, + MouseAction_GrayscaleWindowing + }; + SopClassUid StringToSopClassUid(const std::string& source); void ComputeWindowing(float& targetCenter,
--- a/OrthancStone/Sources/Viewport/DefaultViewportInteractor.cpp Thu Aug 20 13:57:52 2020 +0200 +++ b/OrthancStone/Sources/Viewport/DefaultViewportInteractor.cpp Thu Aug 20 17:44:35 2020 +0200 @@ -25,30 +25,64 @@ #include "../Scene2D/RotateSceneTracker.h" #include "../Scene2D/ZoomSceneTracker.h" +#include <OrthancException.h> + namespace OrthancStone { + IFlexiblePointerTracker* DefaultViewportInteractor::CreateTrackerInternal( + boost::shared_ptr<IViewport> viewport, + MouseAction action, + const PointerEvent& event, + unsigned int viewportWidth, + unsigned int viewportHeight) + { + switch (action) + { + case MouseAction_Rotate: + return new RotateSceneTracker(viewport, event); + + case MouseAction_GrayscaleWindowing: + return new GrayscaleWindowingSceneTracker( + viewport, windowingLayer_, event, viewportWidth, viewportHeight); + + case MouseAction_Pan: + return new PanSceneTracker(viewport, event); + + case MouseAction_Zoom: + return new ZoomSceneTracker(viewport, event, viewportHeight); + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + IFlexiblePointerTracker* DefaultViewportInteractor::CreateTracker( boost::shared_ptr<IViewport> viewport, const PointerEvent& event, unsigned int viewportWidth, unsigned int viewportHeight) { + MouseAction action; + switch (event.GetMouseButton()) { case MouseButton_Left: - //return new RotateSceneTracker(viewport, event); - - return new GrayscaleWindowingSceneTracker( - viewport, windowingLayer_, event, viewportWidth, viewportHeight); + action = leftButtonAction_; + break; case MouseButton_Middle: - return new PanSceneTracker(viewport, event); + action = middleButtonAction_; + break; case MouseButton_Right: - return new ZoomSceneTracker(viewport, event, viewportHeight); + action = rightButtonAction_; + break; default: return NULL; } + + return CreateTrackerInternal(viewport, action, event, viewportWidth, viewportHeight); } }
--- a/OrthancStone/Sources/Viewport/DefaultViewportInteractor.h Thu Aug 20 13:57:52 2020 +0200 +++ b/OrthancStone/Sources/Viewport/DefaultViewportInteractor.h Thu Aug 20 17:44:35 2020 +0200 @@ -27,13 +27,24 @@ class DefaultViewportInteractor : public IViewportInteractor { private: - // Index of the layer whose windowing is altered by clicking the - // left mouse button - int windowingLayer_; + // Index of the layer whose windowing is altered by grayscale windowing action + int windowingLayer_; + MouseAction leftButtonAction_; + MouseAction middleButtonAction_; + MouseAction rightButtonAction_; + + IFlexiblePointerTracker* CreateTrackerInternal(boost::shared_ptr<IViewport> viewport, + MouseAction action, + const PointerEvent& event, + unsigned int viewportWidth, + unsigned int viewportHeight); public: DefaultViewportInteractor() : - windowingLayer_(0) + windowingLayer_(0), + leftButtonAction_(MouseAction_GrayscaleWindowing), + middleButtonAction_(MouseAction_Pan), + rightButtonAction_(MouseAction_Zoom) { } @@ -46,6 +57,36 @@ { windowingLayer_ = layerIndex; } + + MouseAction GetLeftButtonAction() const + { + return leftButtonAction_; + } + + void SetLeftButtonAction(MouseAction action) + { + leftButtonAction_ = action; + } + + MouseAction GetMiddleButtonAction() const + { + return middleButtonAction_; + } + + void SetMiddleButtonAction(MouseAction action) + { + middleButtonAction_ = action; + } + + MouseAction GetRightButtonAction() const + { + return rightButtonAction_; + } + + void SetRightButtonAction(MouseAction action) + { + rightButtonAction_ = action; + } virtual IFlexiblePointerTracker* CreateTracker(boost::shared_ptr<IViewport> viewport, const PointerEvent& event,