# HG changeset patch # User Benjamin Golinvaux # Date 1561365064 -7200 # Node ID e3c56d4f863fb3afc104cdf75b49a210b234d7ab # Parent 41d22389a7d2385ff04167ab76e1b72976b7cf21 GuiAdapter : mouse event routing in SDL + split the undo stack from the ViewportController for multi-canvas apps + adapted the samples to this change diff -r 41d22389a7d2 -r e3c56d4f863f Applications/Generic/GuiAdapter.cpp --- a/Applications/Generic/GuiAdapter.cpp Wed Jun 19 14:12:28 2019 +0200 +++ b/Applications/Generic/GuiAdapter.cpp Mon Jun 24 10:31:04 2019 +0200 @@ -41,6 +41,15 @@ widgets_.push_back(widget); } + std::ostream& operator<<( + std::ostream& os, const GuiAdapterKeyboardEvent& event) + { + os << "ctrl: " << event.ctrlKey << ", " << + "shift: " << event.shiftKey << ", " << + "alt: " << event.altKey; + return os; + } + #if ORTHANC_ENABLE_WASM == 1 void GuiAdapter::Run() { @@ -141,7 +150,8 @@ template struct FuncAdapterPayload { - void* userData; + std::string canvasId; + void* userData; GenericFunc callback; }; @@ -149,7 +159,7 @@ typename GuiAdapterEvent, typename EmscriptenEvent> EM_BOOL OnEventAdapterFunc( - int eventType, const EmscriptenEvent* wheelEvent, void* userData) + int eventType, const EmscriptenEvent* emEvent, void* userData) { // userData is OnMouseWheelFuncAdapterPayload @@ -162,8 +172,8 @@ // " payload->userData: " << payload->userData; GuiAdapterEvent guiEvent; - ConvertFromPlatform(guiEvent, eventType, *wheelEvent); - bool ret = (*(payload->callback))(&guiEvent, payload->userData); + ConvertFromPlatform(guiEvent, eventType, *emEvent); + bool ret = (*(payload->callback))(payload->canvasId, &guiEvent, payload->userData); return static_cast(ret); } @@ -179,7 +189,7 @@ GuiAdapterEvent guiEvent; ConvertFromPlatform(guiEvent, *wheelEvent); - bool ret = (*(payload->callback))(&guiEvent, payload->userData); + bool ret = (*(payload->callback))(payload->canvasId, &guiEvent, payload->userData); return static_cast(ret); } @@ -210,6 +220,7 @@ FuncAdapterPayload* payload = new FuncAdapterPayload(); std::auto_ptr > payloadP(payload); + payload->canvasId = canvasId; payload->callback = func; payload->userData = userData; void* userDataRaw = reinterpret_cast(payload); @@ -236,6 +247,7 @@ std::auto_ptr > payload( new FuncAdapterPayload() ); + payload->canvasId = canvasId; payload->callback = func; payload->userData = userData; void* userDataRaw = reinterpret_cast(payload.release()); @@ -250,14 +262,15 @@ template< typename GenericFunc, typename EmscriptenSetCallbackFunc> - static void SetCallback3( + static void SetAnimationFrameCallback( EmscriptenSetCallbackFunc emFunc, void* userData, GenericFunc func) { - // LOG(ERROR) << "SetCallback3 !!!!!! (RequestAnimationFrame)"; + // LOG(ERROR) << "SetAnimationFrameCallback !!!!!! (RequestAnimationFrame)"; std::auto_ptr > payload( new FuncAdapterPayload() ); + payload->canvasId = "UNDEFINED"; payload->callback = func; payload->userData = userData; void* userDataRaw = reinterpret_cast(payload.release()); @@ -352,7 +365,7 @@ // LOG(ERROR) << "-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"; // LOG(ERROR) << "RequestAnimationFrame"; // LOG(ERROR) << "-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"; - SetCallback3( + SetAnimationFrameCallback( &emscripten_request_animation_frame_loop, userData, func); @@ -384,6 +397,7 @@ #else +// SDL ONLY void ConvertFromPlatform( GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, @@ -401,6 +415,9 @@ case SDL_MOUSEBUTTONUP: dest.type = GUIADAPTER_EVENT_MOUSEUP; break; + case SDL_MOUSEWHEEL: + dest.type = GUIADAPTER_EVENT_WHEEL; + break; default: LOG(ERROR) << "SDL event: " << source.type << " is not supported"; ORTHANC_ASSERT(false, "Not supported"); @@ -441,43 +458,67 @@ //dest.padding = src.padding; } +void ConvertFromPlatform( + GuiAdapterWheelEvent& dest, + bool ctrlPressed, bool shiftPressed, bool altPressed, + const SDL_Event& source) +{ + ConvertFromPlatform(dest.mouse, ctrlPressed, shiftPressed, altPressed, source); + dest.deltaX = source.wheel.x; + dest.deltaY = source.wheel.y; +} + + + + // SDL ONLY void GuiAdapter::SetResizeCallback( std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func) { - resizeHandlers_.push_back(std::make_pair(func, userData)); + resizeHandlers_.push_back(EventHandlerData(canvasId, func, userData)); } + // SDL ONLY void GuiAdapter::SetMouseDownCallback( std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) { + mouseDownHandlers_.push_back(EventHandlerData(canvasId, func, userData)); } + // SDL ONLY void GuiAdapter::SetMouseMoveCallback( std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) { - } + mouseMoveHandlers_.push_back(EventHandlerData(canvasId, func, userData)); + } + // SDL ONLY void GuiAdapter::SetMouseUpCallback( std::string canvasId, void* userData, bool capture, OnMouseEventFunc func) { - } + mouseUpHandlers_.push_back(EventHandlerData(canvasId, func, userData)); + } - void GuiAdapter::SetWheelCallback( + // SDL ONLY + void GuiAdapter::SetWheelCallback( std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func) - { - } + { + mouseWheelHandlers_.push_back(EventHandlerData(canvasId, func, userData)); + } - void GuiAdapter::SetKeyDownCallback( + // SDL ONLY + void GuiAdapter::SetKeyDownCallback( std::string canvasId, void* userData, bool capture, OnKeyDownFunc func) { } - void GuiAdapter::SetKeyUpCallback( + // SDL ONLY + void GuiAdapter::SetKeyUpCallback( std::string canvasId, void* userData, bool capture, OnKeyUpFunc func) { } + // SDL ONLY void GuiAdapter::OnAnimationFrame() { for (size_t i = 0; i < animationFrameHandlers_.size(); i++) @@ -487,20 +528,101 @@ } } + // SDL ONLY void GuiAdapter::OnResize() { for (size_t i = 0; i < resizeHandlers_.size(); i++) { - // TODO: fix time - (*(resizeHandlers_[i].first))(0, resizeHandlers_[i].second); + (*(resizeHandlers_[i].func))( + resizeHandlers_[i].canvasName, 0, resizeHandlers_[i].userData); + } + } + + // SDL ONLY + void GuiAdapter::OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event) + { + + // the SDL window name IS the canvas name ("canvas" is used because this lib + // is designed for Wasm + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + + const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + + std::string windowTitle(windowTitleSz); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + + switch (event.mouse.type) + { + case GUIADAPTER_EVENT_WHEEL: + for (size_t i = 0; i < mouseWheelHandlers_.size(); i++) + { + if(mouseWheelHandlers_[i].canvasName == windowTitle) + (*(mouseWheelHandlers_[i].func))(windowTitle, &event, mouseWheelHandlers_[i].userData); + } + break; + default: + ORTHANC_ASSERT(false, "Wrong event.type: " << event.mouse.type << " in GuiAdapter::OnMouseWheelEvent(...)"); + break; } } - + + // SDL ONLY void GuiAdapter::OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event) { + // the SDL window name IS the canvas name ("canvas" is used because this lib + // is designed for Wasm + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + + const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + + std::string windowTitle(windowTitleSz); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + + switch (event.type) + { + case GUIADAPTER_EVENT_MOUSEDOWN: + for (size_t i = 0; i < mouseDownHandlers_.size(); i++) + { + if (mouseDownHandlers_[i].canvasName == windowTitle) + (*(mouseDownHandlers_[i].func))(windowTitle, &event, mouseDownHandlers_[i].userData); + } + break; + case GUIADAPTER_EVENT_MOUSEMOVE: + for (size_t i = 0; i < mouseMoveHandlers_.size(); i++) + { + if (mouseMoveHandlers_[i].canvasName == windowTitle) + (*(mouseMoveHandlers_[i].func))(windowTitle, &event, mouseMoveHandlers_[i].userData); + } + break; + case GUIADAPTER_EVENT_MOUSEUP: + for (size_t i = 0; i < mouseUpHandlers_.size(); i++) + { + if (mouseUpHandlers_[i].canvasName == windowTitle) + (*(mouseUpHandlers_[i].func))(windowTitle, &event, mouseUpHandlers_[i].userData); + } + break; + default: + ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnMouseEvent(...)"); + break; + } + + ////boost::shared_ptr GetWidgetFromWindowId(); + //boost::shared_ptr foundWidget; + //VisitWidgets([foundWidget, windowID](auto widget) + // { + // if (widget->GetSdlWindowID() == windowID) + // foundWidget = widget; + // }); + //ORTHANC_ASSERT(foundWidget, "WindowID " << windowID << " was not found in the registered widgets!"); + //if(foundWidget) + // foundWidget-> } - + // SDL ONLY void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData) { animationFrameHandlers_.push_back(std::make_pair(func, userData)); @@ -508,6 +630,7 @@ # if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__) /* OpenGL debug is not available on OS X */ + // SDL ONLY static void GLAPIENTRY OpenGLMessageCallback(GLenum source, GLenum type, @@ -526,6 +649,7 @@ } # endif + // SDL ONLY void GuiAdapter::Run() { # if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__) @@ -591,6 +715,44 @@ } #endif } + else if (event.type == SDL_MOUSEWHEEL) + { + + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + bool ctrlPressed(false); + bool shiftPressed(false); + bool altPressed(false); + + if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL]) + ctrlPressed = true; + if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT]) + shiftPressed = true; + if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT]) + altPressed = true; + + GuiAdapterWheelEvent dest; + ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, event); + OnMouseWheelEvent(event.window.windowID, dest); + + //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + //int x, y; + //SDL_GetMouseState(&x, &y); + + //if (event.wheel.y > 0) + //{ + // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers); + //} + //else if (event.wheel.y < 0) + //{ + // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers); + //} + } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { #if 0 diff -r 41d22389a7d2 -r e3c56d4f863f Applications/Generic/GuiAdapter.h --- a/Applications/Generic/GuiAdapter.h Wed Jun 19 14:12:28 2019 +0200 +++ b/Applications/Generic/GuiAdapter.h Mon Jun 24 10:31:04 2019 +0200 @@ -17,6 +17,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . **/ +#pragma once + #include #if ORTHANC_ENABLE_WASM != 1 @@ -62,6 +64,7 @@ { public: virtual ~IGuiAdapterWidget() {} + }; enum GuiAdapterMouseEventType @@ -84,13 +87,13 @@ class LockingEmitter; #if 1 - typedef bool (*OnMouseEventFunc)(const GuiAdapterMouseEvent* mouseEvent, void* userData); - typedef bool (*OnMouseWheelFunc)(const GuiAdapterWheelEvent* wheelEvent, void* userData); - typedef bool (*OnKeyDownFunc) (const GuiAdapterKeyboardEvent* keyEvent, void* userData); - typedef bool (*OnKeyUpFunc) (const GuiAdapterKeyboardEvent* keyEvent, void* userData); + typedef bool (*OnMouseEventFunc)(std::string canvasId, const GuiAdapterMouseEvent* mouseEvent, void* userData); + typedef bool (*OnMouseWheelFunc)(std::string canvasId, const GuiAdapterWheelEvent* wheelEvent, void* userData); + typedef bool (*OnKeyDownFunc) (std::string canvasId, const GuiAdapterKeyboardEvent* keyEvent, void* userData); + typedef bool (*OnKeyUpFunc) (std::string canvasId, const GuiAdapterKeyboardEvent* keyEvent, void* userData); typedef bool (*OnAnimationFrameFunc)(double time, void* userData); - typedef bool (*OnWindowResizeFunc)(const GuiAdapterUiEvent* uiEvent, void* userData); + typedef bool (*OnWindowResizeFunc)(std::string canvasId, const GuiAdapterUiEvent* uiEvent, void* userData); #else @@ -154,6 +157,8 @@ bool altKey; }; + std::ostream& operator<<(std::ostream& os, const GuiAdapterKeyboardEvent& event); + /* Mousedown event trigger when either the left or right (or middle) mouse is pressed on the object; @@ -198,6 +203,12 @@ GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source); + + void ConvertFromPlatform( + GuiAdapterWheelEvent& dest, + bool ctrlPressed, bool shiftPressed, bool altPressed, + const SDL_Event& source); + # endif #endif @@ -221,9 +232,9 @@ /** emscripten_set_resize_callback("#window", NULL, false, OnWindowResize); - emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnMouseWheel); - emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnMouseWheel); - emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnMouseWheel); + emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnXXXMouseWheel); + emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnXXXMouseWheel); + emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnXXXMouseWheel); emscripten_set_keydown_callback("#window", NULL, false, OnKeyDown); emscripten_set_keyup_callback("#window", NULL, false, OnKeyUp); @@ -295,17 +306,41 @@ void OnResize(); - std::vector > - resizeHandlers_; +#if ORTHANC_ENABLE_SDL == 1 + template + struct EventHandlerData + { + EventHandlerData(std::string canvasName, Func func, void* userData) + : canvasName(canvasName) + , func(func) + , userData(userData) + { + } + + std::string canvasName; + Func func; + void* userData; + }; + std::vector > resizeHandlers_; + std::vector > mouseDownHandlers_; + std::vector > mouseMoveHandlers_; + std::vector > mouseUpHandlers_; + std::vector > mouseWheelHandlers_; -#if ORTHANC_ENABLE_SDL == 1 - /** This executes all the registered headers if needed (in wasm, the browser deals with this) */ void OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event); + + /** + Same remark as OnMouseEvent + */ + void OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event); + + boost::shared_ptr GetWidgetFromWindowId(); + #endif /** diff -r 41d22389a7d2 -r e3c56d4f863f Framework/Scene2DViewport/UndoStack.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2DViewport/UndoStack.cpp Mon Jun 24 10:31:04 2019 +0200 @@ -0,0 +1,68 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + +#include "UndoStack.h" + +#include "MeasureCommands.h" + +#include "../StoneException.h" + +namespace OrthancStone +{ + UndoStack::UndoStack() : numAppliedCommands_(0) + {} + + void UndoStack::PushCommand(boost::shared_ptr command) + { + commandStack_.erase( + commandStack_.begin() + numAppliedCommands_, + commandStack_.end()); + + ORTHANC_ASSERT(std::find(commandStack_.begin(), commandStack_.end(), command) + == commandStack_.end(), "Duplicate command"); + commandStack_.push_back(command); + numAppliedCommands_++; + } + + void UndoStack::Undo() + { + ORTHANC_ASSERT(CanUndo(), ""); + commandStack_[numAppliedCommands_ - 1]->Undo(); + numAppliedCommands_--; + } + + void UndoStack::Redo() + { + ORTHANC_ASSERT(CanRedo(), ""); + commandStack_[numAppliedCommands_]->Redo(); + numAppliedCommands_++; + } + + bool UndoStack::CanUndo() const + { + return numAppliedCommands_ > 0; + } + + bool UndoStack::CanRedo() const + { + return numAppliedCommands_ < commandStack_.size(); + } + +} diff -r 41d22389a7d2 -r e3c56d4f863f Framework/Scene2DViewport/UndoStack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2DViewport/UndoStack.h Mon Jun 24 10:31:04 2019 +0200 @@ -0,0 +1,77 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + +#pragma once + +#include + +#include + +namespace OrthancStone +{ + class TrackerCommand; + + class UndoStack + { + public: + UndoStack(); + + /** + Stores a command : + - this first trims the undo stack to keep the first numAppliedCommands_ + - then it adds the supplied command at the top of the undo stack + + In other words, when a new command is pushed, all the undone (and not + redone) commands are removed. + */ + void PushCommand(boost::shared_ptr command); + + /** + Undoes the command at the top of the undo stack, or throws if there is no + command to undo. + You can check "CanUndo" first to protect against extraneous redo. + */ + void Undo(); + + /** + Redoes the command that is just above the last applied command in the undo + stack or throws if there is no command to redo. + You can check "CanRedo" first to protect against extraneous redo. + */ + void Redo(); + + /** selfexpl */ + bool CanUndo() const; + + /** selfexpl */ + bool CanRedo() const; + + private: + std::vector > commandStack_; + + /** + This is always between >= 0 and <= undoStack_.size() and gives the + position where the controller is in the undo stack. + - If numAppliedCommands_ > 0, one can undo + - If numAppliedCommands_ < numAppliedCommands_.size(), one can redo + */ + size_t numAppliedCommands_; + }; +} diff -r 41d22389a7d2 -r e3c56d4f863f Framework/Scene2DViewport/ViewportController.cpp --- a/Framework/Scene2DViewport/ViewportController.cpp Wed Jun 19 14:12:28 2019 +0200 +++ b/Framework/Scene2DViewport/ViewportController.cpp Mon Jun 24 10:31:04 2019 +0200 @@ -19,6 +19,8 @@ **/ #include "ViewportController.h" + +#include "UndoStack.h" #include "MeasureCommands.h" #include "../StoneException.h" @@ -27,14 +29,49 @@ namespace OrthancStone { - ViewportController::ViewportController(MessageBroker& broker) + ViewportController::ViewportController(boost::weak_ptr undoStackW, MessageBroker& broker) : IObservable(broker) - , numAppliedCommands_(0) + , undoStackW_(undoStackW) , canvasToSceneFactor_(0.0) { scene_ = boost::make_shared(); } + boost::shared_ptr ViewportController::GetUndoStack() + { + return undoStackW_.lock(); + } + + boost::shared_ptr ViewportController::GetUndoStack() const + { + return undoStackW_.lock(); + } + + void ViewportController::PushCommand(boost::shared_ptr command) + { + GetUndoStack()->PushCommand(command); + } + + void ViewportController::Undo() + { + GetUndoStack()->Undo(); + } + + void ViewportController::Redo() + { + GetUndoStack()->Redo(); + } + + bool ViewportController::CanUndo() const + { + return GetUndoStack()->CanUndo(); + } + + bool ViewportController::CanRedo() const + { + return GetUndoStack()->CanRedo(); + } + boost::shared_ptr ViewportController::GetScene() const { return scene_; @@ -91,42 +128,6 @@ BroadcastMessage(SceneTransformChanged(*this)); } - void ViewportController::PushCommand(boost::shared_ptr command) - { - commandStack_.erase( - commandStack_.begin() + numAppliedCommands_, - commandStack_.end()); - - ORTHANC_ASSERT(std::find(commandStack_.begin(), commandStack_.end(), command) - == commandStack_.end(), "Duplicate command"); - commandStack_.push_back(command); - numAppliedCommands_++; - } - - void ViewportController::Undo() - { - ORTHANC_ASSERT(CanUndo(), ""); - commandStack_[numAppliedCommands_-1]->Undo(); - numAppliedCommands_--; - } - - void ViewportController::Redo() - { - ORTHANC_ASSERT(CanRedo(), ""); - commandStack_[numAppliedCommands_]->Redo(); - numAppliedCommands_++; - } - - bool ViewportController::CanUndo() const - { - return numAppliedCommands_ > 0; - } - - bool ViewportController::CanRedo() const - { - return numAppliedCommands_ < commandStack_.size(); - } - void ViewportController::AddMeasureTool(boost::shared_ptr measureTool) { ORTHANC_ASSERT(std::find(measureTools_.begin(), measureTools_.end(), measureTool) diff -r 41d22389a7d2 -r e3c56d4f863f Framework/Scene2DViewport/ViewportController.h --- a/Framework/Scene2DViewport/ViewportController.h Wed Jun 19 14:12:28 2019 +0200 +++ b/Framework/Scene2DViewport/ViewportController.h Mon Jun 24 10:31:04 2019 +0200 @@ -30,10 +30,8 @@ namespace OrthancStone { - /** - These constats are used - - */ + class UndoStack; + const double ARC_RADIUS_CANVAS_COORD = 30.0; const double TEXT_CENTER_DISTANCE_CANVAS_COORD = 90; @@ -71,7 +69,7 @@ ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, \ SceneTransformChanged, ViewportController); - ViewportController(MessageBroker& broker); + ViewportController(boost::weak_ptr undoStackW, MessageBroker& broker); boost::shared_ptr GetScene() const; boost::shared_ptr GetScene(); @@ -107,36 +105,6 @@ /** Forwarded to the underlying scene, and broadcasted to the observers */ void FitContent(unsigned int canvasWidth, unsigned int canvasHeight); - /** - Stores a command : - - this first trims the undo stack to keep the first numAppliedCommands_ - - then it adds the supplied command at the top of the undo stack - - In other words, when a new command is pushed, all the undone (and not - redone) commands are removed. - */ - void PushCommand(boost::shared_ptr command); - - /** - Undoes the command at the top of the undo stack, or throws if there is no - command to undo. - You can check "CanUndo" first to protect against extraneous redo. - */ - void Undo(); - - /** - Redoes the command that is just above the last applied command in the undo - stack or throws if there is no command to redo. - You can check "CanRedo" first to protect against extraneous redo. - */ - void Redo(); - - /** selfexpl */ - bool CanUndo() const; - - /** selfexpl */ - bool CanRedo() const; - /** Adds a new measure tool */ void AddMeasureTool(boost::shared_ptr measureTool); @@ -169,18 +137,31 @@ */ double GetAngleTopTextLabelDistanceS() const; + + /** forwarded to the UndoStack */ + void PushCommand(boost::shared_ptr command); + + /** forwarded to the UndoStack */ + void Undo(); + + /** forwarded to the UndoStack */ + void Redo(); + + /** forwarded to the UndoStack */ + bool CanUndo() const; + + /** forwarded to the UndoStack */ + bool CanRedo() const; + + private: double GetCanvasToSceneFactor() const; - std::vector > commandStack_; - - /** - This is always between >= 0 and <= undoStack_.size() and gives the - position where the controller is in the undo stack. - - If numAppliedCommands_ > 0, one can undo - - If numAppliedCommands_ < numAppliedCommands_.size(), one can redo - */ - size_t numAppliedCommands_; + boost::weak_ptr undoStackW_; + + boost::shared_ptr GetUndoStack(); + boost::shared_ptr GetUndoStack() const; + std::vector > measureTools_; boost::shared_ptr scene_; boost::shared_ptr tracker_; diff -r 41d22389a7d2 -r e3c56d4f863f Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Wed Jun 19 14:12:28 2019 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Mon Jun 24 10:31:04 2019 +0200 @@ -491,6 +491,8 @@ ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/OneGesturePointerTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/OneGesturePointerTracker.h ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/PredeclaredTypes.h + ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/UndoStack.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/UndoStack.h ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.cpp ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h ${ORTHANC_STONE_ROOT}/Framework/StoneEnumerations.cpp diff -r 41d22389a7d2 -r e3c56d4f863f Samples/Sdl/BasicScene.cpp --- a/Samples/Sdl/BasicScene.cpp Wed Jun 19 14:12:28 2019 +0200 +++ b/Samples/Sdl/BasicScene.cpp Mon Jun 24 10:31:04 2019 +0200 @@ -29,6 +29,7 @@ #include "../../Framework/Scene2D/Scene2D.h" #include "../../Framework/Scene2D/ZoomSceneTracker.h" #include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" #include "../../Framework/StoneInitialization.h" #include "../../Framework/Messages/MessageBroker.h" @@ -376,8 +377,9 @@ try { MessageBroker broker; + boost::shared_ptr undoStack(new UndoStack); boost::shared_ptr controller = boost::make_shared( - boost::ref(broker)); + undoStack, boost::ref(broker)); PrepareScene(controller); Run(controller); } diff -r 41d22389a7d2 -r e3c56d4f863f Samples/Sdl/FusionMprSdl.cpp --- a/Samples/Sdl/FusionMprSdl.cpp Wed Jun 19 14:12:28 2019 +0200 +++ b/Samples/Sdl/FusionMprSdl.cpp Mon Jun 24 10:31:04 2019 +0200 @@ -31,6 +31,7 @@ #include "../../Framework/Scene2D/ZoomSceneTracker.h" #include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" #include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" #include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" #include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" @@ -407,6 +408,7 @@ , oracleObservable_(broker) , oracle_(*this) , currentTool_(FusionMprGuiTool_Rotate) + , undoStack_(new UndoStack) { //oracleObservable.RegisterObserverCallback //(new Callable @@ -425,7 +427,7 @@ (*this, &FusionMprSdlApp::Handle)); controller_ = boost::shared_ptr( - new ViewportController(broker_)); + new ViewportController(undoStack_, broker_)); controller_->RegisterObserverCallback( new Callable diff -r 41d22389a7d2 -r e3c56d4f863f Samples/Sdl/FusionMprSdl.h --- a/Samples/Sdl/FusionMprSdl.h Wed Jun 19 14:12:28 2019 +0200 +++ b/Samples/Sdl/FusionMprSdl.h Mon Jun 24 10:31:04 2019 +0200 @@ -61,6 +61,7 @@ static const unsigned int FONT_SIZE_1 = 24; class Scene2D; + class UndoStack; /** This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that @@ -194,6 +195,8 @@ int FIXED_INFOTEXT_LAYER_ZINDEX; FusionMprGuiTool currentTool_; + boost::shared_ptr undoStack_; + }; } diff -r 41d22389a7d2 -r e3c56d4f863f Samples/Sdl/TrackerSampleApp.cpp --- a/Samples/Sdl/TrackerSampleApp.cpp Wed Jun 19 14:12:28 2019 +0200 +++ b/Samples/Sdl/TrackerSampleApp.cpp Mon Jun 24 10:31:04 2019 +0200 @@ -29,6 +29,7 @@ #include "../../Framework/Scene2D/RotateSceneTracker.h" #include "../../Framework/Scene2D/Scene2D.h" #include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" #include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" #include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" #include "../../Framework/StoneInitialization.h" @@ -458,8 +459,10 @@ TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker) , currentTool_(GuiTool_Rotate) + , undoStack_(new UndoStack) { - controller_ = boost::shared_ptr(new ViewportController(broker)); + controller_ = boost::shared_ptr( + new ViewportController(undoStack_, broker)); controller_->RegisterObserverCallback( new Callable diff -r 41d22389a7d2 -r e3c56d4f863f Samples/Sdl/TrackerSampleApp.h --- a/Samples/Sdl/TrackerSampleApp.h Wed Jun 19 14:12:28 2019 +0200 +++ b/Samples/Sdl/TrackerSampleApp.h Mon Jun 24 10:31:04 2019 +0200 @@ -52,6 +52,7 @@ static const unsigned int FONT_SIZE_1 = 24; class Scene2D; + class UndoStack; class TrackerSampleApp : public IObserver , public boost::enable_shared_from_this @@ -131,6 +132,7 @@ int FIXED_INFOTEXT_LAYER_ZINDEX; GuiTool currentTool_; + boost::shared_ptr undoStack_; }; }