diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index a37ccf65a..05f1afaf8 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -657,6 +657,7 @@ SET(View3D_CPP_SRCS CADNavigationStyle.cpp BlenderNavigationStyle.cpp TouchpadNavigationStyle.cpp + GestureNavigationStyle.cpp SplitView3DInventor.cpp View.cpp View3DInventor.cpp diff --git a/src/Gui/GestureNavigationStyle.cpp b/src/Gui/GestureNavigationStyle.cpp new file mode 100644 index 000000000..3ac06ed05 --- /dev/null +++ b/src/Gui/GestureNavigationStyle.cpp @@ -0,0 +1,592 @@ +/*************************************************************************** + * Copyright (c) Victor Titov (DeepSOIC) * + * (vv.titov@gmail.com) 2015 * + * * + * 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 * + * * + ***************************************************************************/ + +/* + *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). + */ + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include "InventorAll.h" +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include + +#include +#include "NavigationStyle.h" +#include "View3DInventorViewer.h" +#include "Application.h" +#include "MenuManager.h" +#include "MouseSelection.h" +#include "SoTouchEvents.h" + +using namespace Gui; + +// ---------------------------------------------------------------------------------- + +/* TRANSLATOR Gui::GestureNavigationStyle */ + +TYPESYSTEM_SOURCE(Gui::GestureNavigationStyle, Gui::UserNavigationStyle); + +GestureNavigationStyle::GestureNavigationStyle() +{ + mouseMoveThreshold = QApplication::startDragDistance(); + mouseMoveThresholdBroken = false; + mousedownConsumedCount = 0; + thisClickIsComplex = false; + inGesture = false; +} + +GestureNavigationStyle::~GestureNavigationStyle() +{ +} + +const char* GestureNavigationStyle::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 right mouse button."); + case NavigationStyle::DRAGGING: + return QT_TR_NOOP("Drag the screen with one finger. Or press left mouse button. In Sketcher and other edit modes, hold Alt in addition."); + case NavigationStyle::ZOOMING: + return QT_TR_NOOP("Pinch (put two fingers on the screen and drag them apart/to each other). Or scroll middle mouse button. Or PgUp/PgDown on keyboard."); + default: + return "No description"; + } +} + +/*! + * \brief GestureNavigationStyle::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 GestureNavigationStyle::mouseMoveThresholdBroken flag. + */ +bool GestureNavigationStyle::testMoveThreshold(const SbVec2s currentPos) const { + SbVec2s movedBy = currentPos - this->mousedownPos; + return SbVec2f(movedBy).length() >= this->mouseMoveThreshold; +} + +SbBool GestureNavigationStyle::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. + this->ctrldown = ev->wasCtrlDown(); + this->shiftdown = ev->wasShiftDown(); + this->altdown = ev->wasAltDown(); + //before this block, mouse button states in NavigationStyle::buttonXdown reflected those before current event arrived. + //track mouse button states + if (evIsButton) { + const SoMouseButtonEvent * 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) { + const SoGestureEvent* gesture = static_cast(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) { + const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; + const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; + switch (event->getKey()) { + case SoKeyboardEvent::H: + processed = TRUE; + if(!press){ + SbBool ret = NavigationStyle::lookAtPoint(event->getPosition()); + if(!ret){ + QMessageBox::information(viewer,QObject::tr("Set focus"), + QObject::tr("Aim mouse pointer at a point on some object, and hit H on keyboard. The camera's focus point will jump there.\nIf using touchscreen, tap the point to aim the cursor.")); + } + } + break; + } + } + if (processed) + goto finalize; + + //mode-independent spaceball/joystick handling + if (evIsLoc3) { + const SoMotion3Event * const event = static_cast(ev); + if (event) + this->processMotionEvent(event); + processed = TRUE; + } + if (processed) + goto finalize; + + //all mode-dependent stuff is within this switch. + switch(curmode){ + case NavigationStyle::IDLE: + case NavigationStyle::SELECTION: + case NavigationStyle::INTERACT: { + //idle and interaction + + //keyboard + if (evIsKeyboard) { + const SoKeyboardEvent * 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(), TRUE, posn); + } + processed = TRUE; + break; + case SoKeyboardEvent::PAGE_DOWN: + if(press){ + doZoom(viewer->getSoRenderManager()->getCamera(), FALSE, posn); + } + processed = TRUE; + break; + }//switch key + } + if (processed) + goto finalize; + + + // Mouse Button / Spaceball Button handling + if (evIsButton) { + const SoMouseButtonEvent * 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(button == SoMouseButtonEvent::BUTTON1 && suppressLMBDrag){ + //LMB drag suppressed. The event will be propagated, don't do anything. + } else { + //on left-mouse-button-down, enter dragging mode upon move detetion, or the event will need refiring if the mouse is released with no move. + //reset/start move detection machine + this->mousedownPos = pos; + this->mouseMoveThresholdBroken = false; + pan(viewer->getSoRenderManager()->getCamera());//set up panningplane + int &cnt = this->mousedownConsumedCount; + this->mousedownConsumedEvent[cnt] = *event;//hopefully, a shallow copy is enough. There are no pointers stored in events, apparently. Will loose a subclass, though. + cnt++; + assert(cnt<=2); + if(cnt>sizeof(mousedownConsumedEvent)){ + cnt=sizeof(mousedownConsumedEvent);//we are in trouble + } + processed = true;//just consume this event, and wait for the move threshold to be broken to start dragging/panning + } + } else {//either a release, or another press in a complex click + if (this->mouseMoveThresholdBroken) { + assert(this->mousedownConsumedCount == 0); + //we typically end up here if suppressLMBDrag was true when mousedown. + //do nothing (propagate) + } else { + if (button == SoMouseButtonEvent::BUTTON2 && !this->thisClickIsComplex) { + if (!viewer->isEditing() && this->isPopupMenuEnabled()) { + processed=true; + if (!press) { // release right mouse button + this->openPopupMenu(event->getPosition()); + } + } + } + if(! processed) { + //a left-click without drag (or tap) has happened. + //re-synthesize all previously-consumed mouseDown. + for( int i=0; i < this->mousedownConsumedCount; i++ ){ + inherited::processSoEvent(& (this->mousedownConsumedEvent[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; + } + } // end else of if mouseMoveThresholdBroken + } + break; + case SoMouseButtonEvent::BUTTON3://press the wheel + processed = TRUE; + if(press){ + SbBool ret = NavigationStyle::lookAtPoint(event->getPosition()); + if(!ret){ + //no object under point or other failure. + //ignore... + //QMessageBox::information(viewer,QObject::tr("Set focus"), + // QObject::tr("Aim mouse pointer at a point on some object, and hit H on keyboard. The camera's focus point will jump there.\nIf using touchscreen, tap the point to aim the cursor.")); + } + } + break; + case SoMouseButtonEvent::BUTTON4: //(wheel?) + doZoom(viewer->getSoRenderManager()->getCamera(), TRUE, posn); + processed = TRUE; + break; + case SoMouseButtonEvent::BUTTON5: //(wheel?) + doZoom(viewer->getSoRenderManager()->getCamera(), FALSE, posn); + processed = TRUE; + break; + } + } + + //mouse moves - test for move threshold breaking + if (evIsLoc2) { + if ((this->button1down && !suppressLMBDrag) || this->button2down) { + if (this->mouseMoveThresholdBroken) { + //dupm all consumed mousedowns, we have processed them for navigation. + mousedownConsumedCount = 0; + + setViewingMode(this->button1down ? NavigationStyle::DRAGGING : NavigationStyle::PANNING); + processed = 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. + const SoGestureEvent* gesture = static_cast(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())) { + pan(viewer->getSoRenderManager()->getCamera());//set up panning plane + setViewingMode(NavigationStyle::PANNING); + processed = true; + } else if (type.isDerivedFrom(SoGesturePinchEvent::getClassTypeId())) { + pan(viewer->getSoRenderManager()->getCamera());//set up panning plane + 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) { + const SoMouseButtonEvent * 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(comboAfter & BUTTON1DOWN || comboAfter & BUTTON2DOWN) { + //don't leave navigation till all buttons have been released + setViewingMode((comboAfter & BUTTON1DOWN) ? NavigationStyle::DRAGGING : NavigationStyle::PANNING); + processed = true; + } else { //all buttons are released + //end of dragging/panning/whatever + setViewingMode(NavigationStyle::SELECTION); + processed = true; + } //else of if (some bottons 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) { + const SoLocation2Event * const event = (const SoLocation2Event *) ev; + 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(viewer->getSoRenderManager()->getCamera(), + posn, prevnormalized); + processed = TRUE; + } + } + } + + //the essence part 2! + //gesture into camera motion + if (evIsGesture){ + const SoGestureEvent* gesture = static_cast(ev); + assert(gesture); + if (gesture->state == SoGestureEvent::SbGSEnd) { + setViewingMode(NavigationStyle::SELECTION); + processed=true; + } else if (gesture->state == SoGestureEvent::SbGSUpdate){ + if(type.isDerivedFrom(SoGesturePinchEvent::getClassTypeId())){ + const SoGesturePinchEvent* const event = static_cast(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())){ + const SoGesturePanEvent* const event = static_cast(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; + //This does, unfortunately, happen on regular basis for pan gesture on Windows8.1+Qt4.8 + } + } + + } break;//end of actual navigation + case NavigationStyle::SEEK_WAIT_MODE:{ + if (evIsButton) { + const SoMouseButtonEvent * 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!!! + 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; +} + diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index acbaea135..16446ece6 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -761,6 +761,39 @@ void NavigationStyle::zoomOut() void NavigationStyle::doZoom(SoCamera* camera, SbBool forward, const SbVec2f& pos) { +// SbBool zoomAtCur = this->zoomAtCursor; +// if (zoomAtCur) { +// const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); +// float ratio = vp.getViewportAspectRatio(); +// SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio()); +// SbPlane panplane = vv.getPlane(camera->focalDistance.getValue()); +// panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos); +// } + + float value = this->zoomStep; + if (!forward) + value = -value; + if (this->invertZoom) + value = -value; + doZoom(camera, value, pos); + +// if (zoomAtCur) { +// const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); +// float ratio = vp.getViewportAspectRatio(); +// SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio()); +// SbPlane panplane = vv.getPlane(camera->focalDistance.getValue()); +// panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, pos, SbVec2f(0.5,0.5)); +// } +} + + +/*! + *\brief NavigationStyle::doZoom Zooms in or out by specified factor, keeping the point on screen specified by parameter pos fixed + * or not according to user preference (NavigationStyle::zoomAtCursor). Ignores invertZoom user preference. + */ +void NavigationStyle::doZoom(SoCamera* camera, float logfactor, const SbVec2f& pos) +{ + if (abs(logfactor)>4.0) return;//something is asking for big zoom factor. This func is made for interactive zooming, where the changes are per mouse move and thus are small. SbBool zoomAtCur = this->zoomAtCursor; if (zoomAtCur) { const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); @@ -770,12 +803,7 @@ void NavigationStyle::doZoom(SoCamera* camera, SbBool forward, const SbVec2f& po panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos); } - float value = this->zoomStep; - if (!forward) - value = -value; - if (this->invertZoom) - value = -value; - zoom(camera, value); + zoom(camera, logfactor); if (zoomAtCur) { const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); @@ -786,6 +814,35 @@ void NavigationStyle::doZoom(SoCamera* camera, SbBool forward, const SbVec2f& po } } +void NavigationStyle::doRotate(SoCamera * camera, float angle, const SbVec2f& pos) +{ + SbBool zoomAtCur = this->zoomAtCursor; + if (zoomAtCur) { + const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); + float ratio = vp.getViewportAspectRatio(); + SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio()); + SbPlane panplane = vv.getPlane(camera->focalDistance.getValue()); + panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, SbVec2f(0.5,0.5), pos); + } + + SbRotation rotcam = camera->orientation.getValue(); + //get view direction + SbVec3f vdir; + rotcam.multVec(SbVec3f(0,0,-1),vdir); + //rotate + SbRotation drot(vdir,angle); + camera->orientation.setValue(rotcam * drot); + + if (zoomAtCur) { + const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); + float ratio = vp.getViewportAspectRatio(); + SbViewVolume vv = camera->getViewVolume(vp.getViewportAspectRatio()); + SbPlane panplane = vv.getPlane(camera->focalDistance.getValue()); + panCamera(viewer->getSoRenderManager()->getCamera(), ratio, panplane, pos, SbVec2f(0.5,0.5)); + } + +} + /** Uses the sphere sheet projector to map the mouseposition onto * a 3D point and find a rotation from this and the last calculated point. */ @@ -859,6 +916,40 @@ void NavigationStyle::spin(const SbVec2f & pointerpos) if (this->spinsamplecounter > 3) this->spinsamplecounter = 3; } +/*! + * \brief NavigationStyle::spin_simplified is a simplified version of + * NavigationStyle::spin(..), which uses less global variables. Doesn't support + * starting an animated spinning. + * + * \param cam the camera to affect. The rotation amount is determined by delta + * (curpos-prevpos), and rotation axis is also affected by average pos. + * \param curpos current normalized position or mouse pointer + * \param prevpos previous normalized position of mouse pointer + */ +void NavigationStyle::spin_simplified(SoCamera* cam, SbVec2f curpos, SbVec2f prevpos){ + assert(this->spinprojector != NULL); + + // 0000333: Turntable camera rotation + SbMatrix mat; + viewer->getSoRenderManager()->getCamera()->orientation.getValue().getValue(mat); + this->spinprojector->setWorkingSpace(mat); + + this->spinprojector->project(prevpos); + SbRotation r; + this->spinprojector->projectAndGetRotation(curpos, r); + float sensitivity = getSensitivity(); + if (sensitivity > 1.0f) { + SbVec3f axis; + float radians; + r.getValue(axis, radians); + radians = sensitivity * radians; + r.setValue(axis, radians); + } + r.invert(); + this->reorientCamera(cam, r); + +} + SbBool NavigationStyle::doSpin() { if (this->log.historysize >= 3) { @@ -909,6 +1000,22 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev) } } +SbVec2f NavigationStyle::normalizePixelPos(SbVec2s pixpos) +{ + const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); + const SbVec2s size(vp.getViewportSizePixels()); + return SbVec2f ((float) pixpos[0] / (float) std::max((int)(size[0] - 1), 1), + (float) pixpos[1] / (float) std::max((int)(size[1] - 1), 1)); +} + +SbVec2f NavigationStyle::normalizePixelPos(SbVec2f pixpos) +{ + const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion(); + const SbVec2s size(vp.getViewportSizePixels()); + return SbVec2f ( pixpos[0] / (float) std::max((int)(size[0] - 1), 1), + pixpos[1] / (float) std::max((int)(size[1] - 1), 1)); +} + void NavigationStyle::moveCursorPosition() { if (!isResetCursorPosition()) diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index b801857ea..717597813 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -177,11 +178,17 @@ protected: void zoom(SoCamera * camera, float diffvalue); void zoomByCursor(const SbVec2f & thispos, const SbVec2f & prevpos); void doZoom(SoCamera * camera, SbBool forward, const SbVec2f& pos); + void doZoom(SoCamera * camera, float logzoomfactor, const SbVec2f& pos); + void doRotate(SoCamera * camera, float angle, const SbVec2f& pos); void spin(const SbVec2f & pointerpos); SbBool doSpin(); + void spin_simplified(SoCamera *cam, SbVec2f curpos, SbVec2f prevpos); void moveCursorPosition(); void saveCursorPosition(const SoEvent * const ev); + SbVec2f normalizePixelPos(SbVec2s pixpos); + SbVec2f normalizePixelPos(SbVec2f pixpos); + SbBool handleEventInForeground(const SoEvent* const e); virtual SbBool processSoEvent(const SoEvent * const ev); void syncWithEvent(const SoEvent * const ev); @@ -190,6 +197,7 @@ protected: void clearLog(void); void addToLog(const SbVec2s pos, const SbTime time); + protected: struct { // tracking mouse movement in a log short size; @@ -315,6 +323,30 @@ protected: SbBool processSoEvent(const SoEvent * const ev); }; +class GuiExport GestureNavigationStyle : public UserNavigationStyle { + typedef UserNavigationStyle inherited; + + TYPESYSTEM_HEADER(); + +public: + GestureNavigationStyle(); + ~GestureNavigationStyle(); + const char* mouseButtons(ViewerMode); + +protected: + SbBool processSoEvent(const SoEvent * const ev); + + SbVec2s mousedownPos;//the position where some mouse button was pressed (local pixel coordinates). + short mouseMoveThreshold;//setting. Minimum move required to consider it a move (in pixels). + bool mouseMoveThresholdBroken;//a flag that the move threshold was surpassed since last mousedown. + int mousedownConsumedCount;//a flag for remembering that a mousedown of button1/button2 was consumed. + SoMouseButtonEvent mousedownConsumedEvent[5];//the event that was consumed and is to be refired. 2 should be enough, but just for a case of the maximum 5 buttons... + bool testMoveThreshold(const SbVec2s currentPos) const; + + bool thisClickIsComplex;//a flag that becomes set when a complex clicking pattern is detected (i.e., two or more mouse buttons were down at the same time). + bool inGesture; //a flag that is used to filter out mouse events during gestures. +}; + } // namespace Gui #endif // GUI_NAVIGATIONSTYLE_H diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index f50c89ec4..9a6f8616c 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -134,6 +134,7 @@ void Gui::SoFCDB::init() CADNavigationStyle ::init(); BlenderNavigationStyle ::init(); TouchpadNavigationStyle ::init(); + GestureNavigationStyle ::init(); GLGraphicsItem ::init(); GLFlagWindow ::init();