Download src/Gui/Navigation/MayaGestureNavigationStyle.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 35.7 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Gui/Navigation/MayaGestureNavigationStyle.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Gui/Navigation/MayaGestureNavigationStyle.cpp
-
curl -L -o MayaGestureNavigationStyle.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Gui/Navigation/MayaGestureNavigationStyle.cpp
35.7 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2015 Victor Titov (DeepSOIC) <vv.titov@gmail.com> * | |
| * * | |
| * This file is part of the FreeCAD CAx development system. * | |
| * * | |
| * This library is free software; you can redistribute it and/or * | |
| * modify it under the terms of the GNU Library General Public * | |
| * License as published by the Free Software Foundation; either * | |
| * version 2 of the License, or (at your option) any later version. * | |
| * * | |
| * This library 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 Library General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Library General Public * | |
| * License along with this library; see the file COPYING.LIB. If not, * | |
| * write to the Free Software Foundation, Inc., 59 Temple Place, * | |
| * Suite 330, Boston, MA 02111-1307, USA * | |
| * * | |
| *************************************************************************** | |
| * * | |
| * Minor modifications made by Pablo Gil (pablogil) in order to create * | |
| * a Maya or Unity 3D mouse navigation style: * | |
| * ALT + left mouse button = orbit * | |
| * ALT + right mouse button = zoom * | |
| * ALT + middle mouse button = pan * | |
| * * | |
| * Thanks Victor for your help! * | |
| * * | |
| ***************************************************************************/ | |
| /* | |
| *A few notes on this style. (by DeepSOIC) | |
| * | |
| * In this style, LMB serves dual purpose. It is selecting objects, as well as | |
| * spinning the view. The trick that enables it is as follows: The mousedown | |
| * event is consumed an saved, but otherwise remains unprocessed. If a drag is | |
| * detected while the button is down, the event is finally consumed (the saved | |
| * one is discarded), and spinning starts. If there is no drag detected before | |
| * the button is released, the saved mousedown is propagated to inherited, | |
| * followed by the mouseup. The same trick is used for RMB, so up to two | |
| * mousedown can be postponed. | |
| * | |
| * This navigation style does not exactly follow the structure of other | |
| * navigation styles, it does not fill many of the global variables defined in | |
| * NavigationStyle. | |
| * | |
| * This mode does not support locking cursor position on screen when | |
| * navigating, since with absolute pointing devices like pen and touch it makes | |
| * no sense (this style was specifically crafted for such devices). | |
| * | |
| * In this style, setViewing is not used (because I could not figure out how to | |
| * use it properly, and it seems to just work without it). | |
| * | |
| * This style wasn't tested with space during development (I don't have one). | |
| */ | |
| using namespace Gui; | |
| // ---------------------------------------------------------------------------------- | |
| /* TRANSLATOR Gui::MayaGestureNavigationStyle */ | |
| TYPESYSTEM_SOURCE(Gui::MayaGestureNavigationStyle, Gui::UserNavigationStyle) | |
| MayaGestureNavigationStyle::MayaGestureNavigationStyle() | |
| { | |
| mouseMoveThreshold = QApplication::startDragDistance(); | |
| mouseMoveThresholdBroken = false; | |
| mousedownConsumedCount = 0; | |
| thisClickIsComplex = false; | |
| inGesture = false; | |
| } | |
| MayaGestureNavigationStyle::~MayaGestureNavigationStyle() = default; | |
| const char* MayaGestureNavigationStyle::mouseButtons(ViewerMode mode) | |
| { | |
| switch (mode) { | |
| case NavigationStyle::SELECTION: | |
| return QT_TR_NOOP("Tap OR click left mouse button."); | |
| case NavigationStyle::PANNING: | |
| return QT_TR_NOOP("Drag screen with two fingers OR press Alt + middle mouse button."); | |
| case NavigationStyle::DRAGGING: | |
| return QT_TR_NOOP( | |
| "Drag screen with one finger OR press Alt + left mouse button. In Sketcher and " | |
| "other edit modes, hold Alt in addition." | |
| ); | |
| case NavigationStyle::ZOOMING: | |
| return QT_TR_NOOP( | |
| "Pinch (place two fingers on the screen and drag them apart from or towards each " | |
| "other) OR scroll mouse wheel OR press Alt + right mouse button OR PgUp/PgDown on " | |
| "keyboard." | |
| ); | |
| default: | |
| return "No description"; | |
| } | |
| } | |
| /*! | |
| * \brief MayaGestureNavigationStyle::testMoveThreshold tests if the mouse has moved far enough to | |
| * constder it a drag. | |
| * \param currentPos current position of mouse cursor, in local pixel coordinates. | |
| * \return true if the mouse was moved far enough. False if it's within the boundary. Ignores | |
| * MayaGestureNavigationStyle::mouseMoveThresholdBroken flag. | |
| */ | |
| bool MayaGestureNavigationStyle::testMoveThreshold(const SbVec2s currentPos) const | |
| { | |
| SbVec2s movedBy = currentPos - this->mousedownPos; | |
| return SbVec2f(movedBy).length() >= this->mouseMoveThreshold; | |
| } | |
| void MayaGestureNavigationStyle::zoomByCursor(const SbVec2f& thispos, const SbVec2f& prevpos) | |
| { | |
| const float dx = thispos[0] - prevpos[0]; | |
| const float dy = thispos[1] - prevpos[1]; | |
| // Compute zoom based on diagonal difference. | |
| float value = (dx - dy) * 10.0f; | |
| if (this->invertZoom) { | |
| value = -value; | |
| } | |
| zoom(viewer->getSoRenderManager()->getCamera(), value); | |
| } | |
| SbBool MayaGestureNavigationStyle::processSoEvent(const SoEvent* const ev) | |
| { | |
| // Events when in "ready-to-seek" mode are ignored, except those | |
| // which influence the seek mode itself -- these are handled further | |
| // up the inheritance hierarchy. | |
| if (this->isSeekMode()) { | |
| return inherited::processSoEvent(ev); | |
| } | |
| // Switch off viewing mode (Bug #0000911) | |
| if (!this->isSeekMode() && !this->isAnimating() && this->isViewing()) { | |
| this->setViewing(false); // by default disable viewing mode to render the scene | |
| } | |
| // setViewing() is never used in this style, so the previous if is very unlikely to be hit. | |
| const SoType type(ev->getTypeId()); | |
| // define some shortcuts... | |
| bool evIsButton = type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId()); | |
| bool evIsKeyboard = type.isDerivedFrom(SoKeyboardEvent::getClassTypeId()); | |
| bool evIsLoc2 = type.isDerivedFrom(SoLocation2Event::getClassTypeId()); // mouse movement | |
| bool evIsLoc3 = type.isDerivedFrom(SoMotion3Event::getClassTypeId()); // spaceball/joystick movement | |
| bool evIsGesture = type.isDerivedFrom(SoGestureEvent::getClassTypeId()); // touchscreen gesture | |
| const SbVec2f prevnormalized = this->lastmouseposition; | |
| const SbVec2s pos(ev->getPosition()); // not valid for gestures | |
| const SbVec2f posn = this->normalizePixelPos(pos); | |
| // pos: local coordinates of event, in pixels | |
| // posn: normalized local coordinates of event ((0,0) = lower left corner, (1,1) = upper right | |
| // corner) | |
| float ratio = viewer->getSoRenderManager()->getViewportRegion().getViewportAspectRatio(); | |
| if (evIsButton || evIsLoc2) { | |
| this->lastmouseposition = posn; | |
| } | |
| const ViewerMode curmode = this->currentmode; | |
| // ViewerMode newmode = curmode; | |
| // make a unified mouse+modifiers state value (combo) | |
| enum | |
| { | |
| BUTTON1DOWN = 1 << 0, | |
| BUTTON2DOWN = 1 << 1, | |
| BUTTON3DOWN = 1 << 2, | |
| CTRLDOWN = 1 << 3, | |
| SHIFTDOWN = 1 << 4, | |
| ALTDOWN = 1 << 5, | |
| MASKBUTTONS = BUTTON1DOWN | BUTTON2DOWN | BUTTON3DOWN, | |
| MASKMODIFIERS = CTRLDOWN | SHIFTDOWN | ALTDOWN | |
| }; | |
| unsigned int comboBefore = // before = state before this event | |
| (this->button1down ? BUTTON1DOWN : 0) | (this->button2down ? BUTTON2DOWN : 0) | |
| | (this->button3down ? BUTTON3DOWN : 0) | (this->ctrldown ? CTRLDOWN : 0) | |
| | (this->shiftdown ? SHIFTDOWN : 0) | (this->altdown ? ALTDOWN : 0); | |
| // test for complex clicks | |
| int cntMBBefore = (comboBefore & BUTTON1DOWN ? 1 : 0) // cntMBBefore = how many buttons were | |
| // down when this event arrived? | |
| + (comboBefore & BUTTON2DOWN ? 1 : 0) + (comboBefore & BUTTON3DOWN ? 1 : 0); | |
| if (cntMBBefore >= 2) { | |
| this->thisClickIsComplex = true; | |
| } | |
| if (cntMBBefore == 0) { // a good chance to reset some click-related stuff | |
| this->thisClickIsComplex = false; | |
| this->mousedownConsumedCount = 0; // shouldn't be necessary, just a fail-safe. | |
| } | |
| // Mismatches in state of the modifier keys happens if the user | |
| // presses or releases them outside the viewer window. | |
| syncModifierKeys(ev); | |
| // before this block, mouse button states in NavigationStyle::buttonXdown reflected those before | |
| // current event arrived. track mouse button states | |
| if (evIsButton) { | |
| auto const event = (const SoMouseButtonEvent*)ev; | |
| const int button = event->getButton(); | |
| const SbBool press // the button was pressed (if false -> released) | |
| = event->getState() == SoButtonEvent::DOWN ? true : false; | |
| switch (button) { | |
| case SoMouseButtonEvent::BUTTON1: | |
| this->button1down = press; | |
| break; | |
| case SoMouseButtonEvent::BUTTON2: | |
| this->button2down = press; | |
| break; | |
| case SoMouseButtonEvent::BUTTON3: | |
| this->button3down = press; | |
| break; | |
| // whatever else, we don't track | |
| } | |
| } | |
| // after this block, the new states of the buttons are already in. | |
| unsigned int comboAfter = // after = state after this event (current, essentially) | |
| (this->button1down ? BUTTON1DOWN : 0) | (this->button2down ? BUTTON2DOWN : 0) | |
| | (this->button3down ? BUTTON3DOWN : 0) | (this->ctrldown ? CTRLDOWN : 0) | |
| | (this->shiftdown ? SHIFTDOWN : 0) | (this->altdown ? ALTDOWN : 0); | |
| // test for complex clicks (again) | |
| int cntMBAfter = (comboAfter & BUTTON1DOWN ? 1 : 0) // cntMBAfter = how many buttons were down | |
| // when this event arrived? | |
| + (comboAfter & BUTTON2DOWN ? 1 : 0) + (comboAfter & BUTTON3DOWN ? 1 : 0); | |
| if (cntMBAfter >= 2) { | |
| this->thisClickIsComplex = true; | |
| } | |
| // if (cntMBAfter==0) this->thisClickIsComplex = false;//don't reset the flag now, we need to | |
| // know that this mouseUp was an end of a click that was complex. The flag will reset by the | |
| // before-check in the next event. | |
| // test for move detection | |
| if (evIsLoc2 || evIsButton) { | |
| this->mouseMoveThresholdBroken |= this->testMoveThreshold(pos); | |
| } | |
| // track gestures | |
| if (evIsGesture) { | |
| auto gesture = static_cast<const SoGestureEvent*>(ev); | |
| switch (gesture->state) { | |
| case SoGestureEvent::SbGSStart: | |
| // assert(!inGesture);//start of another gesture before the first finished? Happens | |
| // all the time for Pan gesture... No idea why! --DeepSOIC | |
| inGesture = true; | |
| break; | |
| case SoGestureEvent::SbGSUpdate: | |
| assert(inGesture); // gesture update without start? | |
| inGesture = true; | |
| break; | |
| case SoGestureEvent::SbGSEnd: | |
| assert(inGesture); // gesture ended without starting? | |
| inGesture = false; | |
| break; | |
| case SoGestureEvent::SbGsCanceled: | |
| assert(inGesture); // gesture canceled without starting? | |
| inGesture = false; | |
| break; | |
| default: | |
| assert(0); // shouldn't happen | |
| inGesture = false; | |
| } | |
| } | |
| if (evIsButton) { | |
| if (inGesture) { | |
| inGesture = false; // reset the flag when mouse clicks are received, to ensure enabling | |
| // mouse navigation back. | |
| setViewingMode(NavigationStyle::SELECTION); // exit navigation asap, to proceed with | |
| // regular processing of the click | |
| } | |
| } | |
| bool suppressLMBDrag = false; | |
| if (viewer->isEditing()) { | |
| // in edit mode, disable lmb dragging (spinning). Holding Alt enables it. | |
| suppressLMBDrag = !(comboAfter & ALTDOWN); | |
| } | |
| //----------all this were preparations. Now comes the event handling! ---------- | |
| SbBool processed = false; // a return value for the BlahblahblahNavigationStyle::processSoEvent | |
| bool propagated = false; // an internal flag indicating that the event has been already passed | |
| // to inherited, to suppress the automatic doing of this at the end. | |
| // goto finalize = return processed. Might be important to do something before done (none now). | |
| // give the nodes in the foreground root the chance to handle events (e.g color bar) | |
| if (!viewer->isEditing()) { | |
| processed = handleEventInForeground(ev); | |
| } | |
| if (processed) { | |
| goto finalize; | |
| } | |
| // Mode-independent keyboard handling | |
| if (evIsKeyboard) { | |
| auto const event = (const SoKeyboardEvent*)ev; | |
| const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; | |
| switch (event->getKey()) { | |
| case SoKeyboardEvent::H: | |
| // Disable H key in editing mode because of conflict with sketcher | |
| if (!viewer->isEditing()) { | |
| processed = true; | |
| if (!press) { | |
| setupPanningPlane(viewer->getCamera()); | |
| lookAtPoint(event->getPosition()); | |
| } | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| if (processed) { | |
| goto finalize; | |
| } | |
| // mode-independent spaceball/joystick handling | |
| if (evIsLoc3) { | |
| auto const event = static_cast<const SoMotion3Event*>(ev); | |
| if (event) { | |
| this->processMotionEvent(event); | |
| } | |
| processed = true; | |
| } | |
| if (processed) { | |
| goto finalize; | |
| } | |
| // all mode-dependent stuff is within this switch. | |
| switch (curmode) { | |
| case NavigationStyle::SELECTION: | |
| // Prevent interrupting rubber-band selection in sketcher | |
| if (viewer->isEditing()) { | |
| if (evIsButton) { | |
| auto const event = (const SoMouseButtonEvent*)ev; | |
| const SbBool press = event->getState() == SoButtonEvent::DOWN; | |
| const int button = event->getButton(); | |
| if (!press && button == SoMouseButtonEvent::BUTTON1) { | |
| setViewingMode(NavigationStyle::IDLE); | |
| break; | |
| } | |
| } | |
| if (this->button1down) { | |
| break; | |
| } | |
| } | |
| [[fallthrough]]; | |
| case NavigationStyle::IDLE: | |
| // Prevent interrupting rubber-band selection in sketcher | |
| if (viewer->isEditing()) { | |
| if (evIsButton) { | |
| auto const event = (const SoMouseButtonEvent*)ev; | |
| const SbBool press = event->getState() == SoButtonEvent::DOWN; | |
| const int button = event->getButton(); | |
| if (press && button == SoMouseButtonEvent::BUTTON1 && !this->altdown) { | |
| setViewingMode(NavigationStyle::SELECTION); | |
| break; | |
| } | |
| } | |
| } | |
| [[fallthrough]]; | |
| case NavigationStyle::INTERACT: { | |
| // idle and interaction | |
| // keyboard | |
| if (evIsKeyboard) { | |
| auto const event = (const SoKeyboardEvent*)ev; | |
| const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; | |
| switch (event->getKey()) { | |
| case SoKeyboardEvent::S: | |
| case SoKeyboardEvent::HOME: | |
| case SoKeyboardEvent::LEFT_ARROW: | |
| case SoKeyboardEvent::UP_ARROW: | |
| case SoKeyboardEvent::RIGHT_ARROW: | |
| case SoKeyboardEvent::DOWN_ARROW: | |
| processed = inherited::processSoEvent(ev); | |
| propagated = true; | |
| break; | |
| case SoKeyboardEvent::PAGE_UP: | |
| if (press) { | |
| doZoom(viewer->getSoRenderManager()->getCamera(), getDelta(), posn); | |
| } | |
| processed = true; | |
| break; | |
| case SoKeyboardEvent::PAGE_DOWN: | |
| if (press) { | |
| doZoom(viewer->getSoRenderManager()->getCamera(), -getDelta(), posn); | |
| } | |
| processed = true; | |
| break; | |
| default: | |
| break; | |
| } // switch key | |
| } | |
| if (processed) { | |
| goto finalize; | |
| } | |
| // Mouse Button / Spaceball Button handling | |
| if (evIsButton) { | |
| auto const event = (const SoMouseButtonEvent*)ev; | |
| const int button = event->getButton(); | |
| const SbBool press // the button was pressed (if false -> released) | |
| = event->getState() == SoButtonEvent::DOWN ? true : false; | |
| switch (button) { | |
| case SoMouseButtonEvent::BUTTON1: | |
| case SoMouseButtonEvent::BUTTON2: | |
| if (press) { | |
| if (this->thisClickIsComplex && this->mouseMoveThresholdBroken) { | |
| // this should prevent re-attempts to enter navigation when doing | |
| // more clicks after a move. | |
| } | |
| else { | |
| // on LMB-down or RMB-down, we don't know yet if we should propagate | |
| // it or process it. Save the event to be refired later, when it | |
| // becomes clear. reset/start move detection machine | |
| this->mousedownPos = pos; | |
| this->mouseMoveThresholdBroken = false; | |
| setupPanningPlane( | |
| viewer->getSoRenderManager()->getCamera() | |
| ); // set up panningplane | |
| int& cnt = this->mousedownConsumedCount; | |
| this->mousedownConsumedEvents[cnt] | |
| = *event; // hopefully, a shallow copy is enough. There are no | |
| // pointers stored in events, apparently. Will lose a | |
| // subclass, though. | |
| cnt++; | |
| assert(cnt <= 2); | |
| if (cnt > static_cast<int>(sizeof(mousedownConsumedEvents))) { | |
| cnt = sizeof(mousedownConsumedEvents); // we are in trouble | |
| } | |
| processed = true; // just consume this event, and wait for the move | |
| // threshold to be broken to start dragging/panning | |
| } | |
| } | |
| else { // release | |
| if (button == SoMouseButtonEvent::BUTTON2 && !this->thisClickIsComplex) { | |
| if (!viewer->isEditing() && this->isPopupMenuEnabled()) { | |
| processed = true; | |
| this->openPopupMenu(event->getPosition()); | |
| } | |
| } | |
| if (!processed) { | |
| // re-synthesize all previously-consumed mouseDowns, if any. They | |
| // might have been re-synthesized already when threshold was broken. | |
| for (int i = 0; i < this->mousedownConsumedCount; i++) { | |
| inherited::processSoEvent( | |
| &(this->mousedownConsumedEvents[i]) | |
| ); // simulate the previously-comsumed mousedown. | |
| } | |
| this->mousedownConsumedCount = 0; | |
| processed = inherited::processSoEvent( | |
| ev | |
| ); // explicitly, just for clarity that we are sending a full click | |
| // sequence. | |
| propagated = true; | |
| } | |
| } | |
| break; | |
| case SoMouseButtonEvent::BUTTON3: // press the wheel | |
| // starts PANNING mode | |
| if (press & this->altdown) { | |
| setViewingMode(NavigationStyle::PANNING); | |
| } | |
| else if (press) { | |
| // if not PANNING then look at point | |
| setupPanningPlane(viewer->getCamera()); | |
| lookAtPoint(event->getPosition()); | |
| } | |
| processed = true; | |
| break; | |
| } | |
| } | |
| // mouse moves - test for move threshold breaking | |
| if (evIsLoc2) { | |
| if (this->mouseMoveThresholdBroken && (this->button1down || this->button2down) | |
| && mousedownConsumedCount > 0) { | |
| // mousemovethreshold has JUST been broken | |
| // test if we should enter navigation | |
| if ((this->button1down && !suppressLMBDrag && this->altdown) | |
| || (this->button2down && this->altdown)) { | |
| // yes, we are entering navigation. | |
| // throw away consumed mousedowns. | |
| this->mousedownConsumedCount = 0; | |
| // start DRAGGING mode (orbit) | |
| // if not pressing left mouse button then it assumes is right mouse button | |
| // and starts ZOOMING mode | |
| saveCursorPosition(ev); | |
| setViewingMode( | |
| this->button1down ? NavigationStyle::DRAGGING : NavigationStyle::ZOOMING | |
| ); | |
| processed = true; | |
| } | |
| else { | |
| // no, we are not entering navigation. | |
| // re-synthesize all previously-consumed mouseDowns, if any, and propagate | |
| // this mousemove. | |
| for (int i = 0; i < this->mousedownConsumedCount; i++) { | |
| inherited::processSoEvent( | |
| &(this->mousedownConsumedEvents[i]) | |
| ); // simulate the previously-comsumed mousedown. | |
| } | |
| this->mousedownConsumedCount = 0; | |
| processed = inherited::processSoEvent(ev); // explicitly, just for clarity | |
| // that we are sending a full | |
| // click sequence. | |
| propagated = true; | |
| } | |
| } | |
| if (mousedownConsumedCount > 0) { | |
| processed = true; // if we are still deciding if it's a drag or not, consume | |
| // mouseMoves. | |
| } | |
| } | |
| // gesture start | |
| if (evIsGesture | |
| && /*!this->button1down &&*/ !this->button2down) { // ignore gestures when mouse | |
| // buttons are down. Button1down | |
| // check was disabled because of | |
| // wrong state after doubleclick | |
| // on sketcher constraint to | |
| // edit datum | |
| auto gesture = static_cast<const SoGestureEvent*>(ev); | |
| if (gesture->state == SoGestureEvent::SbGSStart | |
| || gesture->state == SoGestureEvent::SbGSUpdate) { // even if we didn't get a | |
| // start, assume the first | |
| // update is a start | |
| // (sort-of fail-safe). | |
| if (type.isDerivedFrom(SoGesturePanEvent::getClassTypeId())) { | |
| setupPanningPlane(viewer->getSoRenderManager()->getCamera()); // set up | |
| // panning | |
| // plane | |
| setViewingMode(NavigationStyle::PANNING); | |
| processed = true; | |
| } | |
| else if (type.isDerivedFrom(SoGesturePinchEvent::getClassTypeId())) { | |
| setupPanningPlane(viewer->getSoRenderManager()->getCamera()); // set up | |
| // panning | |
| // plane | |
| saveCursorPosition(ev); | |
| setViewingMode(NavigationStyle::DRAGGING); | |
| processed = true; | |
| } // all other gestures - ignore! | |
| } | |
| } | |
| // loc2 (mousemove) - ignore. | |
| } break; // end of idle and interaction | |
| case NavigationStyle::DRAGGING: | |
| case NavigationStyle::ZOOMING: | |
| case NavigationStyle::PANNING: { | |
| // actual navigation | |
| // no keyboard. | |
| // Mouse Button / Spaceball Button handling | |
| if (evIsButton) { | |
| auto const event = (const SoMouseButtonEvent*)ev; | |
| const int button = event->getButton(); | |
| switch (button) { | |
| case SoMouseButtonEvent::BUTTON1: | |
| case SoMouseButtonEvent::BUTTON2: | |
| case SoMouseButtonEvent::BUTTON3: // allows one to release button3 into | |
| // SELECTION mode | |
| if (comboAfter & BUTTON1DOWN || comboAfter & BUTTON2DOWN) { | |
| // don't leave navigation till all buttons have been released | |
| if (comboAfter & BUTTON1DOWN && comboAfter & BUTTON2DOWN) { | |
| setRotationCenter(getFocalPoint()); | |
| } | |
| else { | |
| saveCursorPosition(ev); | |
| } | |
| setViewingMode( | |
| (comboAfter & BUTTON1DOWN) ? NavigationStyle::DRAGGING | |
| : NavigationStyle::PANNING | |
| ); | |
| processed = true; | |
| } | |
| else { // all buttons are released | |
| // end of dragging/panning/whatever | |
| setViewingMode(NavigationStyle::IDLE); | |
| processed = true; | |
| } // end of else (some buttons down) | |
| break; | |
| } // switch(button) | |
| } // if(evIsButton) | |
| // the essence part 1! | |
| // mouse movement into camera motion. Suppress if in gesture. Ignore until threshold is | |
| // surpassed. | |
| if (evIsLoc2 && !this->inGesture && this->mouseMoveThresholdBroken) { | |
| if (curmode == NavigationStyle::ZOOMING) { // doesn't happen | |
| this->zoomByCursor(posn, prevnormalized); | |
| processed = true; | |
| } | |
| else if (curmode == NavigationStyle::PANNING) { | |
| panCamera( | |
| viewer->getSoRenderManager()->getCamera(), | |
| ratio, | |
| this->panningplane, | |
| posn, | |
| prevnormalized | |
| ); | |
| processed = true; | |
| } | |
| else if (curmode == NavigationStyle::DRAGGING) { | |
| if (comboAfter & BUTTON1DOWN && comboAfter & BUTTON2DOWN) { | |
| // two mouse buttons down - tilting! | |
| NavigationStyle::doRotate( | |
| viewer->getSoRenderManager()->getCamera(), | |
| (posn - prevnormalized)[0] * (-2), | |
| SbVec2f(0.5, 0.5) | |
| ); | |
| processed = true; | |
| } | |
| else { // one mouse button - normal spinning | |
| // this will also handle the single-finger drag (there's no gesture used, | |
| // pseudomouse is enough) this->addToLog(event->getPosition(), event->getTime()); | |
| this->spin_simplified(posn, prevnormalized); | |
| processed = true; | |
| } | |
| } | |
| } | |
| // the essence part 2! | |
| // gesture into camera motion | |
| if (evIsGesture) { | |
| auto gesture = static_cast<const SoGestureEvent*>(ev); | |
| assert(gesture); | |
| if (gesture->state == SoGestureEvent::SbGSEnd) { | |
| setViewingMode(NavigationStyle::SELECTION); | |
| processed = true; | |
| } | |
| else if (gesture->state == SoGestureEvent::SbGSUpdate) { | |
| if (type.isDerivedFrom(SoGesturePinchEvent::getClassTypeId())) { | |
| auto const event = static_cast<const SoGesturePinchEvent*>(ev); | |
| if (this->zoomAtCursor) { | |
| // this is just dealing with the pan part of pinch gesture. Taking care | |
| // of zooming to pos is done in doZoom. | |
| SbVec2f panDist = this->normalizePixelPos(event->deltaCenter.getValue()); | |
| NavigationStyle::panCamera( | |
| viewer->getSoRenderManager()->getCamera(), | |
| ratio, | |
| this->panningplane, | |
| panDist, | |
| SbVec2f(0, 0) | |
| ); | |
| } | |
| NavigationStyle::doZoom( | |
| viewer->getSoRenderManager()->getCamera(), | |
| -logf(event->deltaZoom), | |
| this->normalizePixelPos(event->curCenter) | |
| ); | |
| if (event->deltaAngle != 0) { | |
| NavigationStyle::doRotate( | |
| viewer->getSoRenderManager()->getCamera(), | |
| event->deltaAngle, | |
| this->normalizePixelPos(event->curCenter) | |
| ); | |
| } | |
| processed = true; | |
| } | |
| if (type.isDerivedFrom(SoGesturePanEvent::getClassTypeId())) { | |
| auto const event = static_cast<const SoGesturePanEvent*>(ev); | |
| // this is just dealing with the pan part of pinch gesture. Taking care of | |
| // zooming to pos is done in doZoom. | |
| SbVec2f panDist = this->normalizePixelPos(event->deltaOffset); | |
| NavigationStyle::panCamera( | |
| viewer->getSoRenderManager()->getCamera(), | |
| ratio, | |
| this->panningplane, | |
| panDist, | |
| SbVec2f(0, 0) | |
| ); | |
| processed = true; | |
| } | |
| } | |
| else { | |
| // shouldn't happen. Gestures are not expected to start in the middle of | |
| // navigation. we'll consume it, without reacting. | |
| processed = true; | |
| } | |
| } | |
| } break; // end of actual navigation | |
| case NavigationStyle::SEEK_WAIT_MODE: { | |
| if (evIsButton) { | |
| auto const event = (const SoMouseButtonEvent*)ev; | |
| const int button = event->getButton(); | |
| const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; | |
| if (button == SoMouseButtonEvent::BUTTON1 && press) { | |
| this->seekToPoint(pos); // implicitly calls interactiveCountInc() | |
| this->setViewingMode(NavigationStyle::SEEK_MODE); | |
| processed = true; | |
| } | |
| } | |
| }; // not end of SEEK_WAIT_MODE. Fall through by design!!! | |
| /* FALLTHRU */ | |
| case NavigationStyle::SPINNING: | |
| case NavigationStyle::SEEK_MODE: { | |
| // animation modes | |
| if (!processed) { | |
| if (evIsButton || evIsGesture || evIsKeyboard || evIsLoc3) { | |
| setViewingMode(NavigationStyle::SELECTION); | |
| } | |
| } | |
| } break; // end of animation modes | |
| case NavigationStyle::BOXZOOM: | |
| default: | |
| // all the rest - will be pass on to inherited, later. | |
| break; | |
| } | |
| if (!processed && !propagated) { | |
| processed = inherited::processSoEvent(ev); | |
| propagated = true; | |
| } | |
| //-----------------------end of event handling--------------------- | |
| finalize: | |
| return processed; | |
| } | |