From a1730a626c5143b5e25bb685b97534a0a1a00e7f Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 22 Apr 2025 19:27:06 +0200 Subject: [PATCH 1/6] Gui: Implement navigation style for NX See forum threads: https://forum.freecad.org/viewtopic.php?t=96459 or https://forum.freecad.org/viewtopic.php?t=96503 --- src/Gui/CMakeLists.txt | 1 + src/Gui/Navigation/NavigationStyle.h | 32 ++ src/Gui/Navigation/SiemensNXStyle.cpp | 639 ++++++++++++++++++++++++++ src/Gui/SoFCDB.cpp | 1 + 4 files changed, 673 insertions(+) create mode 100644 src/Gui/Navigation/SiemensNXStyle.cpp diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index b3fff2e403..d6b7d6850d 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -938,6 +938,7 @@ SET(Navigation_CPP_SRCS Navigation/CADNavigationStyle.cpp Navigation/RevitNavigationStyle.cpp Navigation/BlenderNavigationStyle.cpp + Navigation/SiemensNXStyle.cpp Navigation/SolidWorksNavigationStyle.cpp Navigation/MayaGestureNavigationStyle.cpp Navigation/OpenCascadeNavigationStyle.cpp diff --git a/src/Gui/Navigation/NavigationStyle.h b/src/Gui/Navigation/NavigationStyle.h index be54296471..e606b179b9 100644 --- a/src/Gui/Navigation/NavigationStyle.h +++ b/src/Gui/Navigation/NavigationStyle.h @@ -51,6 +51,7 @@ class SoCamera; class SoSensor; class SbSphereSheetProjector; +// NOLINTBEGIN(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) namespace Gui { class View3DInventorViewer; @@ -489,7 +490,38 @@ protected: SbBool processSoEvent(const SoEvent * const ev) override; }; +class GuiExport SiemensNXStyle : public UserNavigationStyle { + using inherited = UserNavigationStyle; + + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + SiemensNXStyle(); + ~SiemensNXStyle() override; + const char* mouseButtons(ViewerMode mode) override; + std::string userFriendlyName() const override; + +protected: + SbBool processSoEvent(const SoEvent * const ev) override; + SbBool processKeyboardEvent(const SoKeyboardEvent * const event) override; + +private: + struct Event; + struct NaviMachine; + struct IdleState; + struct AwaitingReleaseState; + struct AwaitingMoveState; + struct InteractState; + struct RotateState; + struct PanState; + struct ZoomState; + struct SelectionState; + + std::unique_ptr naviMachine; +}; + } // namespace Gui +// NOLINTEND(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) Q_DECLARE_OPERATORS_FOR_FLAGS(Gui::NavigationStyle::RotationCenterModes) diff --git a/src/Gui/Navigation/SiemensNXStyle.cpp b/src/Gui/Navigation/SiemensNXStyle.cpp new file mode 100644 index 0000000000..4420321e26 --- /dev/null +++ b/src/Gui/Navigation/SiemensNXStyle.cpp @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif + +#include +#include +#include + +#include "Camera.h" +#include "NavigationStyle.h" +#include "View3DInventorViewer.h" + +// NOLINTBEGIN(cppcoreguidelines-pro-type-static-cast-downcast, +// cppcoreguidelines-avoid*, +// readability-avoid-const-params-in-decls) +using namespace Gui; +namespace sc = boost::statechart; +using NS = SiemensNXStyle; + +struct NS::Event: public sc::event +{ + Event() : flags(new Flags){} + using Button = SoMouseButtonEvent::Button; + using Key = SoKeyboardEvent::Key; + bool isMouseButtonEvent() const { + return this->inventor_event->isOfType(SoMouseButtonEvent::getClassTypeId()); + } + const SoMouseButtonEvent* asMouseButtonEvent() const { + return static_cast(this->inventor_event); + } + bool isPress(Button button) const { + return SoMouseButtonEvent::isButtonPressEvent(this->inventor_event, button); + } + bool isRelease(Button button) const { + return SoMouseButtonEvent::isButtonReleaseEvent(this->inventor_event, button); + } + bool isKeyPress(Key key) const { + return SoKeyboardEvent::isKeyPressEvent(this->inventor_event, key); + } + bool isKeyRelease(Key key) const { + return SoKeyboardEvent::isKeyReleaseEvent(this->inventor_event, key); + } + bool isKeyboardEvent() const { + return this->inventor_event->isOfType(SoKeyboardEvent::getClassTypeId()); + } + const SoKeyboardEvent* asKeyboardEvent() const { + return static_cast(this->inventor_event); + } + bool isLocation2Event() const { + return this->inventor_event->isOfType(SoLocation2Event::getClassTypeId()); + } + const SoLocation2Event* asLocation2Event() const { + return static_cast(this->inventor_event); + } + bool isMotion3Event() const { + return this->inventor_event->isOfType(SoMotion3Event::getClassTypeId()); + } + const SoMotion3Event* asMotion3Event() const { + return static_cast(this->inventor_event); + } + bool isDownButton(unsigned int state) const { + return mbstate() == state; + } + bool isDownNoButton() const { + return mbstate() == 0; + } + bool isDownButton1() const { + return (mbstate() & BUTTON1DOWN) == BUTTON1DOWN; + } + bool isDownButton2() const { + return (mbstate() & BUTTON2DOWN) == BUTTON2DOWN; + } + bool isDownButton3() const { + return (mbstate() & BUTTON3DOWN) == BUTTON3DOWN; + } + bool isDownControl() const { + return (kbstate() & CTRLDOWN) == CTRLDOWN; + } + bool isDownShift() const { + return (kbstate() & SHIFTDOWN) == SHIFTDOWN; + } + bool isDownAlt() const { + return (kbstate() & ALTDOWN) == ALTDOWN; + } + + enum { + // bits: 0-shift-ctrl-alt-0-lmb-mmb-rmb + BUTTON1DOWN = 0x00000100, + BUTTON2DOWN = 0x00000001, + BUTTON3DOWN = 0x00000010, + CTRLDOWN = 0x00100000, + SHIFTDOWN = 0x01000000, + ALTDOWN = 0x00010000, + MASKBUTTONS = BUTTON1DOWN | BUTTON2DOWN | BUTTON3DOWN, + MASKMODIFIERS = CTRLDOWN | SHIFTDOWN | ALTDOWN + }; + + const SoEvent* inventor_event{nullptr}; + unsigned int modifiers{0}; + unsigned int mbstate() const {return modifiers & MASKBUTTONS;} + unsigned int kbstate() const {return modifiers & MASKMODIFIERS;} + + struct Flags{ + bool processed = false; + bool propagated = false; + }; + std::shared_ptr flags; +}; + +struct NS::NaviMachine: public sc::state_machine +{ + using superclass = sc::state_machine; + explicit NaviMachine(NS& ns) : ns(ns) {} + NS& ns; +}; + +struct NS::IdleState: public sc::state +{ + using reactions = sc::custom_reaction; + explicit IdleState(my_context ctx) : my_base(ctx) + { + auto &ns = this->outermost_context().ns; + ns.setViewingMode(NavigationStyle::IDLE); + } + sc::result react(const NS::Event& ev) + { + auto &ns = this->outermost_context().ns; + switch (ns.getViewingMode()) { + case NavigationStyle::SEEK_WAIT_MODE: + { + if (ev.isPress(SoMouseButtonEvent::BUTTON1)) { + ns.seekToPoint(ev.inventor_event->getPosition()); + ns.setViewingMode(NavigationStyle::SEEK_MODE); + ev.flags->processed = true; + return transit(); + } + break; + } + case NavigationStyle::SPINNING: + case NavigationStyle::SEEK_MODE: + { + if (!ev.flags->processed) { + if (ev.isMouseButtonEvent()) { + ev.flags->processed = true; + return transit(); + } + else if (ev.isKeyboardEvent() || ev.isMotion3Event()) { + ns.setViewingMode(NavigationStyle::IDLE); + } + } + + break; + } + case NavigationStyle::BOXZOOM: + return forward_event(); + } + + // right-click + if (ev.isRelease(SoMouseButtonEvent::BUTTON2) + && ev.mbstate() == 0 + && !ns.viewer->isEditing() + && ns.isPopupMenuEnabled()){ + ns.openPopupMenu(ev.inventor_event->getPosition()); + } + + if (ev.isPress(SoMouseButtonEvent::BUTTON3)) { + if (ev.isDownShift()) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isDownButton(NS::Event::BUTTON3DOWN)) { + ev.flags->processed = true; + return transit(); + } + } + + // Use processClickEvent() + + // Implement selection callback + //if (ev.isLocation2Event() && ev.isDownButton1()) { + // ev.flags->processed = true; + // return transit(); + //} + + return forward_event(); + } +}; + +struct NS::AwaitingReleaseState : public sc::state +{ + using reactions = sc::custom_reaction; + explicit AwaitingReleaseState(my_context ctx) : my_base(ctx) + {} + + sc::result react(const NS::Event& /*ev*/) + { + return forward_event(); + } +}; + +struct NS::InteractState: public sc::state +{ + using reactions = sc::custom_reaction; + explicit InteractState(my_context ctx):my_base(ctx) + { + auto &ns = this->outermost_context().ns; + ns.setViewingMode(NavigationStyle::INTERACT); + } + + sc::result react(const NS::Event& /*ev*/) { + return forward_event(); + } +}; + +struct NS::AwaitingMoveState: public sc::state +{ + using reactions = sc::custom_reaction; + +private: + SbVec2s base_pos; + SbTime since; + +public: + explicit AwaitingMoveState(my_context ctx):my_base(ctx) + { + auto &ns = this->outermost_context().ns; + ns.setViewingMode(NavigationStyle::DRAGGING); + this->base_pos = static_cast(this->triggering_event())->inventor_event->getPosition(); + this->since = static_cast(this->triggering_event())->inventor_event->getTime(); + } + sc::result react(const NS::Event& ev){ + //this state consumes all mouse events. + ev.flags->processed = ev.isMouseButtonEvent() || ev.isLocation2Event(); + + if (ev.isLocation2Event()) { + return transit(); + } + + // right-click + if (ev.isPress(SoMouseButtonEvent::BUTTON2) && ev.isDownButton3()) { + return transit(); + } + + if (ev.isKeyPress(SoKeyboardEvent::LEFT_SHIFT)) { + ev.flags->processed = true; + return transit(); + } + + // left-click + if (ev.isPress(SoMouseButtonEvent::BUTTON1) && ev.isDownButton3()) { + return transit(); + } + + if (ev.isKeyPress(SoKeyboardEvent::LEFT_CONTROL)) { + ev.flags->processed = true; + return transit(); + } + + // middle-click + if (ev.isRelease(SoMouseButtonEvent::BUTTON3) && ev.isDownNoButton()) { + auto &ns = this->outermost_context().ns; + SbTime tmp = (ev.inventor_event->getTime() - this->since); + double dci = QApplication::doubleClickInterval() / 1000.0; + + // is this a simple middle click? + if (tmp.getValue() < dci) { + ev.flags->processed = true; + SbVec2s pos = ev.inventor_event->getPosition(); + ns.lookAtPoint(pos); + } + return transit(); + } + + return forward_event(); + } +}; + +struct NS::RotateState: public sc::state +{ + using reactions = sc::custom_reaction; + explicit RotateState(my_context ctx) : my_base(ctx) + { + auto &ns = this->outermost_context().ns; + const auto inventorEvent = static_cast(this->triggering_event())->inventor_event; + ns.saveCursorPosition(inventorEvent); + ns.setViewingMode(NavigationStyle::DRAGGING); + this->base_pos = inventorEvent->getPosition(); + } + + sc::result react(const NS::Event& ev) + { + if (ev.isLocation2Event()) { + auto &ns = this->outermost_context().ns; + ns.addToLog(ev.inventor_event->getPosition(), ev.inventor_event->getTime()); + const SbVec2s pos = ev.inventor_event->getPosition(); + const SbVec2f posn = ns.normalizePixelPos(pos); + ns.spin(posn); + ns.moveCursorPosition(); + ev.flags->processed = true; + } + + // right-click + if (ev.isPress(SoMouseButtonEvent::BUTTON2) && ev.isDownButton3()) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isKeyPress(SoKeyboardEvent::LEFT_SHIFT)) { + ev.flags->processed = true; + return transit(); + } + + // left-click + if (ev.isPress(SoMouseButtonEvent::BUTTON1) && ev.isDownButton3()) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isKeyPress(SoKeyboardEvent::LEFT_CONTROL)) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isRelease(SoMouseButtonEvent::BUTTON3) && ev.isDownNoButton()) { + ev.flags->processed = true; + return transit(); + } + return forward_event(); + } + +private: + SbVec2s base_pos; +}; + +struct NS::PanState: public sc::state +{ + using reactions = sc::custom_reaction; + explicit PanState(my_context ctx):my_base(ctx) + { + auto &ns = this->outermost_context().ns; + const NS::Event* ev = static_cast(this->triggering_event()); + ns.setViewingMode(NavigationStyle::PANNING); + this->base_pos = ev->inventor_event->getPosition(); + this->ratio = ns.viewer->getSoRenderManager()->getViewportRegion().getViewportAspectRatio(); + ns.centerTime = ev->inventor_event->getTime(); + ns.setupPanningPlane(ns.getCamera()); + } + sc::result react(const NS::Event& ev) + { + if (ev.isLocation2Event()){ + ev.flags->processed = true; + SbVec2s pos = ev.inventor_event->getPosition(); + auto &ns = this->outermost_context().ns; + ns.panCamera(ns.viewer->getSoRenderManager()->getCamera(), + this->ratio, + ns.panningplane, + ns.normalizePixelPos(pos), + ns.normalizePixelPos(this->base_pos)); + this->base_pos = pos; + } + + if (ev.isRelease(SoMouseButtonEvent::BUTTON2) && ev.isDownButton3()) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isKeyRelease(SoKeyboardEvent::LEFT_SHIFT) && ev.isDownButton3()) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isRelease(SoMouseButtonEvent::BUTTON3)) { + ev.flags->processed = true; + return transit(); + } + + return forward_event(); + } + +private: + SbVec2s base_pos; + float ratio {1.0F}; +}; + +struct NS::ZoomState: public sc::state +{ + using reactions = sc::custom_reaction; + explicit ZoomState(my_context ctx) : my_base(ctx) + { + auto &ns = this->outermost_context().ns; + const NS::Event* ev = static_cast(this->triggering_event()); + ns.setViewingMode(NavigationStyle::ZOOMING); + this->base_pos = ev->inventor_event->getPosition(); + } + + sc::result react(const NS::Event& ev) + { + if (ev.isLocation2Event()){ + ev.flags->processed = true; + SbVec2s pos = ev.inventor_event->getPosition(); + auto &ns = this->outermost_context().ns; + ns.zoomByCursor(ns.normalizePixelPos(pos), + ns.normalizePixelPos(this->base_pos)); + this->base_pos = pos; + } + + if (ev.isRelease(SoMouseButtonEvent::BUTTON1) && ev.isDownButton3()) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isKeyRelease(SoKeyboardEvent::LEFT_CONTROL) && ev.isDownButton3()) { + ev.flags->processed = true; + return transit(); + } + + if (ev.isRelease(SoMouseButtonEvent::BUTTON3)) { + ev.flags->processed = true; + return transit(); + } + + return forward_event(); + } + +private: + SbVec2s base_pos; +}; + +struct NS::SelectionState: public sc::state +{ + using reactions = sc::custom_reaction; + explicit SelectionState(my_context ctx) : my_base(ctx) + { + auto &ns = this->outermost_context().ns; + const NS::Event* ev = static_cast(this->triggering_event()); + + ns.setViewingMode(NavigationStyle::BOXZOOM); + ns.startSelection(NavigationStyle::Rubberband); + fakeLeftButtonDown(ev->inventor_event->getPosition()); + } + + void fakeLeftButtonDown(const SbVec2s& pos) + { + SoMouseButtonEvent mbe; + mbe.setButton(SoMouseButtonEvent::BUTTON1); + mbe.setState(SoMouseButtonEvent::DOWN); + mbe.setPosition(pos); + + auto &ns = this->outermost_context().ns; + ns.processEvent(&mbe); + } + + sc::result react(const NS::Event& /*ev*/) + { + // This isn't called while selection mode is active + return transit(); + } +}; + +// ---------------------------------------------------------------------------------- + +/* TRANSLATOR Gui::SiemensNXStyle */ + +TYPESYSTEM_SOURCE(Gui::SiemensNXStyle, Gui::UserNavigationStyle) + +SiemensNXStyle::SiemensNXStyle() + : naviMachine(new NS::NaviMachine(*this)) +{ + naviMachine->initiate(); +} + +SiemensNXStyle::~SiemensNXStyle() +{ + naviMachine.reset(); +} + +const char* SiemensNXStyle::mouseButtons(ViewerMode mode) +{ + switch (mode) { + case NavigationStyle::SELECTION: + return QT_TR_NOOP("Press left mouse button"); + case NavigationStyle::PANNING: + return QT_TR_NOOP("Press middle+right click"); + case NavigationStyle::DRAGGING: + return QT_TR_NOOP("Press middle mouse button"); + case NavigationStyle::ZOOMING: + return QT_TR_NOOP("Scroll mouse wheel"); + default: + return "No description"; + } +} + +std::string SiemensNXStyle::userFriendlyName() const +{ + return {"Siemens NX"}; +} + +SbBool SiemensNXStyle::processKeyboardEvent(const SoKeyboardEvent * const event) +{ + // See https://forum.freecad.org/viewtopic.php?t=96459 + // Isometric view: Home key button + // Trimetric view: End key button + // Fit all: CTRL+F + // Normal view: F8 + switch (event->getKey()) { + case SoKeyboardEvent::F: + if (event->wasCtrlDown()) { + viewer->viewAll(); + return true; + } + break; + case SoKeyboardEvent::HOME: + { + viewer->setCameraOrientation(Camera::rotation(Camera::Isometric)); + return true; + } + case SoKeyboardEvent::END: + { + viewer->setCameraOrientation(Camera::rotation(Camera::Trimetric)); + return true; + } + case SoKeyboardEvent::F8: + { + viewer->setCameraOrientation(Camera::rotation(Camera::Top)); + return true; + } + default: + break; + } + + return inherited::processKeyboardEvent(event); +} + +SbBool SiemensNXStyle::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 + } + + // Mismatches in state of the modifier keys happens if the user + // presses or releases them outside the viewer window. + syncModifierKeys(ev); + + // give the nodes in the foreground root the chance to handle events (e.g color bar) + if (!viewer->isEditing()) { + if (handleEventInForeground(ev)) { + return true; + } + } + + NS::Event smev; + smev.inventor_event = ev; + + // Spaceball/joystick handling + if (ev->isOfType(SoMotion3Event::getClassTypeId())){ + smev.flags->processed = true; + this->processMotionEvent(static_cast(ev)); + return true; + } + + // Keyboard handling + if (ev->isOfType(SoKeyboardEvent::getClassTypeId())) { + const auto event = static_cast(ev); + smev.flags->processed = processKeyboardEvent(event); + } + + if (smev.isMouseButtonEvent()) { + const auto button = smev.asMouseButtonEvent()->getButton(); + const SbBool press = smev.asMouseButtonEvent()->getState() == SoButtonEvent::DOWN; + switch (button) { + case SoMouseButtonEvent::BUTTON1: + this->button1down = press; + break; + case SoMouseButtonEvent::BUTTON2: + this->button2down = press; + break; + case SoMouseButtonEvent::BUTTON3: + this->button3down = press; + break; + default: + break; + } + } + + smev.modifiers = + (this->button1down ? NS::Event::BUTTON1DOWN : 0) | + (this->button2down ? NS::Event::BUTTON2DOWN : 0) | + (this->button3down ? NS::Event::BUTTON3DOWN : 0) | + (this->ctrldown ? NS::Event::CTRLDOWN : 0) | + (this->shiftdown ? NS::Event::SHIFTDOWN : 0) | + (this->altdown ? NS::Event::ALTDOWN : 0); + + if (!smev.flags->processed) { + this->naviMachine->process_event(smev); + } + + if (!smev.flags->propagated && !smev.flags->processed) { + return inherited::processSoEvent(ev); + } + + return smev.flags->processed; +} +// NOLINTEND(cppcoreguidelines-pro-type-static-cast-downcast, +// cppcoreguidelines-avoid*, +// readability-avoid-const-params-in-decls) diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 29e80fbe31..11e1a76212 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -199,6 +199,7 @@ void Gui::SoFCDB::init() OpenCascadeNavigationStyle ::init(); OpenSCADNavigationStyle ::init(); TinkerCADNavigationStyle ::init(); + SiemensNXStyle ::init(); GLGraphicsItem ::init(); GLFlagWindow ::init(); From ba9c09fc6144d75384e121597959e947aa069498 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 23 Apr 2025 13:01:49 +0200 Subject: [PATCH 2/6] Gui: Refactor navigation styles Add new base class for state chart based navigation styles --- src/Gui/CMakeLists.txt | 2 + src/Gui/Navigation/NavigationStateChart.cpp | 233 ++++++++++++++++++++ src/Gui/Navigation/NavigationStateChart.h | 148 +++++++++++++ src/Gui/SoFCDB.cpp | 2 + 4 files changed, 385 insertions(+) create mode 100644 src/Gui/Navigation/NavigationStateChart.cpp create mode 100644 src/Gui/Navigation/NavigationStateChart.h diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index d6b7d6850d..f793a48ebd 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -932,6 +932,7 @@ SET(View3D_SRCS SOURCE_GROUP("View3D" FILES ${View3D_SRCS}) SET(Navigation_CPP_SRCS + Navigation/NavigationStateChart.cpp Navigation/NavigationStyle.cpp Navigation/NavigationStylePyImp.cpp Navigation/InventorNavigationStyle.cpp @@ -951,6 +952,7 @@ SET(Navigation_CPP_SRCS ) SET(Navigation_SRCS ${Navigation_CPP_SRCS} + Navigation/NavigationStateChart.h Navigation/NavigationStyle.h Navigation/NavigationStyle.pyi Navigation/GestureNavigationStyle.h diff --git a/src/Gui/Navigation/NavigationStateChart.cpp b/src/Gui/Navigation/NavigationStateChart.cpp new file mode 100644 index 0000000000..c146fbb9a6 --- /dev/null +++ b/src/Gui/Navigation/NavigationStateChart.cpp @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" +#include +#include +#include + +#include "Camera.h" +#include "NavigationStateChart.h" +#include "View3DInventorViewer.h" + + +using namespace Gui; +namespace sc = boost::statechart; +using NS = NavigationStateChart; + +NS::Event::Event() : flags(new Flags) +{} + +bool NS::Event::isMouseButtonEvent() const +{ + return this->inventor_event->isOfType(SoMouseButtonEvent::getClassTypeId()); +} + +const SoMouseButtonEvent* NS::Event::asMouseButtonEvent() const +{ + return static_cast(this->inventor_event); +} + +bool NS::Event::isPress(Button button) const +{ + return SoMouseButtonEvent::isButtonPressEvent(this->inventor_event, button); +} + +bool NS::Event::isRelease(Button button) const +{ + return SoMouseButtonEvent::isButtonReleaseEvent(this->inventor_event, button); +} + +bool NS::Event::isKeyPress(Key key) const +{ + return SoKeyboardEvent::isKeyPressEvent(this->inventor_event, key); +} + +bool NS::Event::isKeyRelease(Key key) const +{ + return SoKeyboardEvent::isKeyReleaseEvent(this->inventor_event, key); +} + +bool NS::Event::isKeyboardEvent() const +{ + return this->inventor_event->isOfType(SoKeyboardEvent::getClassTypeId()); +} + +const SoKeyboardEvent* NS::Event::asKeyboardEvent() const +{ + return static_cast(this->inventor_event); +} + +bool NS::Event::isLocation2Event() const +{ + return this->inventor_event->isOfType(SoLocation2Event::getClassTypeId()); +} + +const SoLocation2Event* NS::Event::asLocation2Event() const +{ + return static_cast(this->inventor_event); +} + +bool NS::Event::isMotion3Event() const +{ + return this->inventor_event->isOfType(SoMotion3Event::getClassTypeId()); +} + +const SoMotion3Event* NS::Event::asMotion3Event() const +{ + return static_cast(this->inventor_event); +} + +bool NS::Event::isDownButton(unsigned int state) const +{ + return mbstate() == state; +} + +bool NS::Event::isDownNoButton() const +{ + return mbstate() == 0; +} + +bool NS::Event::isDownButton1() const +{ + return (mbstate() & BUTTON1DOWN) == BUTTON1DOWN; +} + +bool NS::Event::isDownButton2() const +{ + return (mbstate() & BUTTON2DOWN) == BUTTON2DOWN; +} + +bool NS::Event::isDownButton3() const +{ + return (mbstate() & BUTTON3DOWN) == BUTTON3DOWN; +} + +bool NS::Event::isDownControl() const +{ + return (kbstate() & CTRLDOWN) == CTRLDOWN; +} + +bool NS::Event::isDownShift() const +{ + return (kbstate() & SHIFTDOWN) == SHIFTDOWN; +} + +bool NS::Event::isDownAlt() const +{ + return (kbstate() & ALTDOWN) == ALTDOWN; +} + + +/* TRANSLATOR Gui::NavigationStateChart */ + +TYPESYSTEM_SOURCE_ABSTRACT(Gui::NavigationStateChart, Gui::UserNavigationStyle) + +NavigationStateChart::NavigationStateChart() + : naviMachine(new NS::StateMachine()) +{ +} + +NavigationStateChart::~NavigationStateChart() +{ + naviMachine.reset(); +} + +SbBool NavigationStateChart::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 + } + + // Mismatches in state of the modifier keys happens if the user + // presses or releases them outside the viewer window. + syncModifierKeys(ev); + + // give the nodes in the foreground root the chance to handle events (e.g color bar) + if (!viewer->isEditing()) { + if (handleEventInForeground(ev)) { + return true; + } + } + + NS::Event smev; + smev.inventor_event = ev; + + // Spaceball/joystick handling + if (ev->isOfType(SoMotion3Event::getClassTypeId())){ + smev.flags->processed = true; + this->processMotionEvent(static_cast(ev)); + return true; + } + + // Keyboard handling + if (ev->isOfType(SoKeyboardEvent::getClassTypeId())) { + const auto event = static_cast(ev); + smev.flags->processed = processKeyboardEvent(event); + } + + if (smev.isMouseButtonEvent()) { + const auto button = smev.asMouseButtonEvent()->getButton(); + const SbBool press = smev.asMouseButtonEvent()->getState() == SoButtonEvent::DOWN; + switch (button) { + case SoMouseButtonEvent::BUTTON1: + this->button1down = press; + break; + case SoMouseButtonEvent::BUTTON2: + this->button2down = press; + break; + case SoMouseButtonEvent::BUTTON3: + this->button3down = press; + break; + default: + break; + } + } + + smev.modifiers = + (this->button1down ? NS::Event::BUTTON1DOWN : 0) | + (this->button2down ? NS::Event::BUTTON2DOWN : 0) | + (this->button3down ? NS::Event::BUTTON3DOWN : 0) | + (this->ctrldown ? NS::Event::CTRLDOWN : 0) | + (this->shiftdown ? NS::Event::SHIFTDOWN : 0) | + (this->altdown ? NS::Event::ALTDOWN : 0); + + if (!smev.flags->processed) { + this->naviMachine->process_event(smev); + } + + if (!smev.flags->propagated && !smev.flags->processed) { + return inherited::processSoEvent(ev); + } + + return smev.flags->processed; +} diff --git a/src/Gui/Navigation/NavigationStateChart.h b/src/Gui/Navigation/NavigationStateChart.h new file mode 100644 index 0000000000..f7e7a50008 --- /dev/null +++ b/src/Gui/Navigation/NavigationStateChart.h @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef GUI_NAVIGATIONSTATECHART_H +#define GUI_NAVIGATIONSTATECHART_H + +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) +namespace Gui +{ + +class GuiExport NavigationStateChart : public UserNavigationStyle { + using inherited = UserNavigationStyle; + + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + struct Event: public boost::statechart::event + { + using Button = SoMouseButtonEvent::Button; + using Key = SoKeyboardEvent::Key; + + Event(); + bool isMouseButtonEvent() const; + const SoMouseButtonEvent* asMouseButtonEvent() const; + bool isPress(Button button) const; + bool isRelease(Button button) const; + bool isKeyPress(Key key) const; + bool isKeyRelease(Key key) const; + bool isKeyboardEvent() const; + const SoKeyboardEvent* asKeyboardEvent() const; + bool isLocation2Event() const; + const SoLocation2Event* asLocation2Event() const; + bool isMotion3Event() const; + const SoMotion3Event* asMotion3Event() const; + bool isDownButton(unsigned int state) const; + bool isDownNoButton() const; + bool isDownButton1() const; + bool isDownButton2() const; + bool isDownButton3() const; + bool isDownControl() const; + bool isDownShift() const; + bool isDownAlt() const; + + enum { + // bits: 0-shift-ctrl-alt-0-lmb-mmb-rmb + BUTTON1DOWN = 0x00000100, + BUTTON2DOWN = 0x00000001, + BUTTON3DOWN = 0x00000010, + CTRLDOWN = 0x00100000, + SHIFTDOWN = 0x01000000, + ALTDOWN = 0x00010000, + MASKBUTTONS = BUTTON1DOWN | BUTTON2DOWN | BUTTON3DOWN, + MASKMODIFIERS = CTRLDOWN | SHIFTDOWN | ALTDOWN + }; + + const SoEvent* inventor_event{nullptr}; + unsigned int modifiers{0}; + unsigned int mbstate() const {return modifiers & MASKBUTTONS;} + unsigned int kbstate() const {return modifiers & MASKMODIFIERS;} + + struct Flags{ + bool processed = false; + bool propagated = false; + }; + std::shared_ptr flags; + }; + + class StateMachine + { + public: + template + void make_object(T* obj) + { + object = std::make_shared>(obj); + } + + void process_event(const Event& ev) + { + if (object) { + object->process_event(ev); + } + } + + struct Concept + { + virtual ~Concept() = default; + virtual void process_event(const Event&) = 0; + }; + + template< typename T > + struct Model : Concept + { + explicit Model(T* t) : object(t) + { + object->initiate(); + } + ~Model() override + { + object.reset(); + } + void process_event(const Event& ev) override + { + object->process_event(ev); + } + + private: + std::shared_ptr object; + }; + + std::shared_ptr object; + }; + + NavigationStateChart(); + ~NavigationStateChart() override; + +protected: + SbBool processSoEvent(const SoEvent * const ev) override; + std::unique_ptr naviMachine; // NOLINT +}; + +} // namespace Gui +// NOLINTEND(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) + +#endif // GUI_NAVIGATIONSTATECHART_H diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 11e1a76212..1e9e927075 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -53,6 +53,7 @@ #include "Inventor/Draggers/SoTransformDragger.h" #include "Navigation/GestureNavigationStyle.h" #include "Navigation/NavigationStyle.h" +#include "Navigation/NavigationStateChart.h" #include "SelectionObject.h" #include "SoDevicePixelRatioElement.h" #include "SoFCColorBar.h" @@ -188,6 +189,7 @@ void Gui::SoFCDB::init() NavigationStyle ::init(); UserNavigationStyle ::init(); + NavigationStateChart ::init(); InventorNavigationStyle ::init(); CADNavigationStyle ::init(); RevitNavigationStyle ::init(); From 00e05045df0dc9315cf6ee214db9dbaaac82a781 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 23 Apr 2025 13:49:23 +0200 Subject: [PATCH 3/6] Gui: Refactor navigation styles Derive SiemensNXStyle from NavigationStateChart --- src/Gui/CMakeLists.txt | 1 + src/Gui/Navigation/NavigationStyle.h | 30 ----- src/Gui/Navigation/SiemensNXStyle.cpp | 180 +------------------------- src/Gui/Navigation/SiemensNXStyle.h | 63 +++++++++ src/Gui/SoFCDB.cpp | 2 +- 5 files changed, 71 insertions(+), 205 deletions(-) create mode 100644 src/Gui/Navigation/SiemensNXStyle.h diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index f793a48ebd..49391e1c2b 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -958,6 +958,7 @@ SET(Navigation_SRCS Navigation/GestureNavigationStyle.h Navigation/NavigationAnimator.h Navigation/NavigationAnimation.h + Navigation/SiemensNXStyle.h ) SOURCE_GROUP("Navigation" FILES ${Navigation_SRCS}) diff --git a/src/Gui/Navigation/NavigationStyle.h b/src/Gui/Navigation/NavigationStyle.h index e606b179b9..152b6f030a 100644 --- a/src/Gui/Navigation/NavigationStyle.h +++ b/src/Gui/Navigation/NavigationStyle.h @@ -490,36 +490,6 @@ protected: SbBool processSoEvent(const SoEvent * const ev) override; }; -class GuiExport SiemensNXStyle : public UserNavigationStyle { - using inherited = UserNavigationStyle; - - TYPESYSTEM_HEADER_WITH_OVERRIDE(); - -public: - SiemensNXStyle(); - ~SiemensNXStyle() override; - const char* mouseButtons(ViewerMode mode) override; - std::string userFriendlyName() const override; - -protected: - SbBool processSoEvent(const SoEvent * const ev) override; - SbBool processKeyboardEvent(const SoKeyboardEvent * const event) override; - -private: - struct Event; - struct NaviMachine; - struct IdleState; - struct AwaitingReleaseState; - struct AwaitingMoveState; - struct InteractState; - struct RotateState; - struct PanState; - struct ZoomState; - struct SelectionState; - - std::unique_ptr naviMachine; -}; - } // namespace Gui // NOLINTEND(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) diff --git a/src/Gui/Navigation/SiemensNXStyle.cpp b/src/Gui/Navigation/SiemensNXStyle.cpp index 4420321e26..98721dee98 100644 --- a/src/Gui/Navigation/SiemensNXStyle.cpp +++ b/src/Gui/Navigation/SiemensNXStyle.cpp @@ -32,7 +32,7 @@ #include #include "Camera.h" -#include "NavigationStyle.h" +#include "SiemensNXStyle.h" #include "View3DInventorViewer.h" // NOLINTBEGIN(cppcoreguidelines-pro-type-static-cast-downcast, @@ -40,98 +40,9 @@ // readability-avoid-const-params-in-decls) using namespace Gui; namespace sc = boost::statechart; +using SC = NavigationStateChart; using NS = SiemensNXStyle; -struct NS::Event: public sc::event -{ - Event() : flags(new Flags){} - using Button = SoMouseButtonEvent::Button; - using Key = SoKeyboardEvent::Key; - bool isMouseButtonEvent() const { - return this->inventor_event->isOfType(SoMouseButtonEvent::getClassTypeId()); - } - const SoMouseButtonEvent* asMouseButtonEvent() const { - return static_cast(this->inventor_event); - } - bool isPress(Button button) const { - return SoMouseButtonEvent::isButtonPressEvent(this->inventor_event, button); - } - bool isRelease(Button button) const { - return SoMouseButtonEvent::isButtonReleaseEvent(this->inventor_event, button); - } - bool isKeyPress(Key key) const { - return SoKeyboardEvent::isKeyPressEvent(this->inventor_event, key); - } - bool isKeyRelease(Key key) const { - return SoKeyboardEvent::isKeyReleaseEvent(this->inventor_event, key); - } - bool isKeyboardEvent() const { - return this->inventor_event->isOfType(SoKeyboardEvent::getClassTypeId()); - } - const SoKeyboardEvent* asKeyboardEvent() const { - return static_cast(this->inventor_event); - } - bool isLocation2Event() const { - return this->inventor_event->isOfType(SoLocation2Event::getClassTypeId()); - } - const SoLocation2Event* asLocation2Event() const { - return static_cast(this->inventor_event); - } - bool isMotion3Event() const { - return this->inventor_event->isOfType(SoMotion3Event::getClassTypeId()); - } - const SoMotion3Event* asMotion3Event() const { - return static_cast(this->inventor_event); - } - bool isDownButton(unsigned int state) const { - return mbstate() == state; - } - bool isDownNoButton() const { - return mbstate() == 0; - } - bool isDownButton1() const { - return (mbstate() & BUTTON1DOWN) == BUTTON1DOWN; - } - bool isDownButton2() const { - return (mbstate() & BUTTON2DOWN) == BUTTON2DOWN; - } - bool isDownButton3() const { - return (mbstate() & BUTTON3DOWN) == BUTTON3DOWN; - } - bool isDownControl() const { - return (kbstate() & CTRLDOWN) == CTRLDOWN; - } - bool isDownShift() const { - return (kbstate() & SHIFTDOWN) == SHIFTDOWN; - } - bool isDownAlt() const { - return (kbstate() & ALTDOWN) == ALTDOWN; - } - - enum { - // bits: 0-shift-ctrl-alt-0-lmb-mmb-rmb - BUTTON1DOWN = 0x00000100, - BUTTON2DOWN = 0x00000001, - BUTTON3DOWN = 0x00000010, - CTRLDOWN = 0x00100000, - SHIFTDOWN = 0x01000000, - ALTDOWN = 0x00010000, - MASKBUTTONS = BUTTON1DOWN | BUTTON2DOWN | BUTTON3DOWN, - MASKMODIFIERS = CTRLDOWN | SHIFTDOWN | ALTDOWN - }; - - const SoEvent* inventor_event{nullptr}; - unsigned int modifiers{0}; - unsigned int mbstate() const {return modifiers & MASKBUTTONS;} - unsigned int kbstate() const {return modifiers & MASKMODIFIERS;} - - struct Flags{ - bool processed = false; - bool propagated = false; - }; - std::shared_ptr flags; -}; - struct NS::NaviMachine: public sc::state_machine { using superclass = sc::state_machine; @@ -141,13 +52,13 @@ struct NS::NaviMachine: public sc::state_machine struct NS::IdleState: public sc::state { - using reactions = sc::custom_reaction; + using reactions = sc::custom_reaction; explicit IdleState(my_context ctx) : my_base(ctx) { auto &ns = this->outermost_context().ns; ns.setViewingMode(NavigationStyle::IDLE); } - sc::result react(const NS::Event& ev) + sc::result react(const SC::Event& ev) { auto &ns = this->outermost_context().ns; switch (ns.getViewingMode()) { @@ -194,7 +105,7 @@ struct NS::IdleState: public sc::state return transit(); } - if (ev.isDownButton(NS::Event::BUTTON3DOWN)) { + if (ev.isDownButton(SC::Event::BUTTON3DOWN)) { ev.flags->processed = true; return transit(); } @@ -490,14 +401,12 @@ struct NS::SelectionState: public sc::state TYPESYSTEM_SOURCE(Gui::SiemensNXStyle, Gui::UserNavigationStyle) SiemensNXStyle::SiemensNXStyle() - : naviMachine(new NS::NaviMachine(*this)) { - naviMachine->initiate(); + naviMachine->make_object(new NS::NaviMachine(*this)); } SiemensNXStyle::~SiemensNXStyle() { - naviMachine.reset(); } const char* SiemensNXStyle::mouseButtons(ViewerMode mode) @@ -557,83 +466,6 @@ SbBool SiemensNXStyle::processKeyboardEvent(const SoKeyboardEvent * const event) return inherited::processKeyboardEvent(event); } -SbBool SiemensNXStyle::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 - } - - // Mismatches in state of the modifier keys happens if the user - // presses or releases them outside the viewer window. - syncModifierKeys(ev); - - // give the nodes in the foreground root the chance to handle events (e.g color bar) - if (!viewer->isEditing()) { - if (handleEventInForeground(ev)) { - return true; - } - } - - NS::Event smev; - smev.inventor_event = ev; - - // Spaceball/joystick handling - if (ev->isOfType(SoMotion3Event::getClassTypeId())){ - smev.flags->processed = true; - this->processMotionEvent(static_cast(ev)); - return true; - } - - // Keyboard handling - if (ev->isOfType(SoKeyboardEvent::getClassTypeId())) { - const auto event = static_cast(ev); - smev.flags->processed = processKeyboardEvent(event); - } - - if (smev.isMouseButtonEvent()) { - const auto button = smev.asMouseButtonEvent()->getButton(); - const SbBool press = smev.asMouseButtonEvent()->getState() == SoButtonEvent::DOWN; - switch (button) { - case SoMouseButtonEvent::BUTTON1: - this->button1down = press; - break; - case SoMouseButtonEvent::BUTTON2: - this->button2down = press; - break; - case SoMouseButtonEvent::BUTTON3: - this->button3down = press; - break; - default: - break; - } - } - - smev.modifiers = - (this->button1down ? NS::Event::BUTTON1DOWN : 0) | - (this->button2down ? NS::Event::BUTTON2DOWN : 0) | - (this->button3down ? NS::Event::BUTTON3DOWN : 0) | - (this->ctrldown ? NS::Event::CTRLDOWN : 0) | - (this->shiftdown ? NS::Event::SHIFTDOWN : 0) | - (this->altdown ? NS::Event::ALTDOWN : 0); - - if (!smev.flags->processed) { - this->naviMachine->process_event(smev); - } - - if (!smev.flags->propagated && !smev.flags->processed) { - return inherited::processSoEvent(ev); - } - - return smev.flags->processed; -} // NOLINTEND(cppcoreguidelines-pro-type-static-cast-downcast, // cppcoreguidelines-avoid*, // readability-avoid-const-params-in-decls) diff --git a/src/Gui/Navigation/SiemensNXStyle.h b/src/Gui/Navigation/SiemensNXStyle.h new file mode 100644 index 0000000000..efd36afadc --- /dev/null +++ b/src/Gui/Navigation/SiemensNXStyle.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2025 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef GUI_SIEMENSNXSTYLE_H +#define GUI_SIEMENSNXSTYLE_H + +#include + +// NOLINTBEGIN(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) +namespace Gui +{ + +class GuiExport SiemensNXStyle : public NavigationStateChart { + using inherited = NavigationStateChart; + + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + SiemensNXStyle(); + ~SiemensNXStyle() override; + const char* mouseButtons(ViewerMode mode) override; + std::string userFriendlyName() const override; + +protected: + SbBool processKeyboardEvent(const SoKeyboardEvent * const event) override; + +private: + struct NaviMachine; + struct IdleState; + struct AwaitingReleaseState; + struct AwaitingMoveState; + struct InteractState; + struct RotateState; + struct PanState; + struct ZoomState; + struct SelectionState; +}; + +} // namespace Gui +// NOLINTEND(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) + +#endif // GUI_SIEMENSNXSTYLE_H diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 1e9e927075..ab6f365df4 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -53,7 +53,7 @@ #include "Inventor/Draggers/SoTransformDragger.h" #include "Navigation/GestureNavigationStyle.h" #include "Navigation/NavigationStyle.h" -#include "Navigation/NavigationStateChart.h" +#include "Navigation/SiemensNXStyle.h" #include "SelectionObject.h" #include "SoDevicePixelRatioElement.h" #include "SoFCColorBar.h" From 969c147be2948ab6eb9ccc5f23149ddab407e1c3 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 23 Apr 2025 14:12:19 +0200 Subject: [PATCH 4/6] Gui: Refactor navigation styles Simplify type erasure --- src/Gui/Navigation/NavigationStateChart.cpp | 4 +- src/Gui/Navigation/NavigationStateChart.h | 86 ++++++++++----------- src/Gui/Navigation/SiemensNXStyle.cpp | 2 +- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/Gui/Navigation/NavigationStateChart.cpp b/src/Gui/Navigation/NavigationStateChart.cpp index c146fbb9a6..70a12f8f67 100644 --- a/src/Gui/Navigation/NavigationStateChart.cpp +++ b/src/Gui/Navigation/NavigationStateChart.cpp @@ -145,9 +145,7 @@ bool NS::Event::isDownAlt() const TYPESYSTEM_SOURCE_ABSTRACT(Gui::NavigationStateChart, Gui::UserNavigationStyle) NavigationStateChart::NavigationStateChart() - : naviMachine(new NS::StateMachine()) -{ -} +{} NavigationStateChart::~NavigationStateChart() { diff --git a/src/Gui/Navigation/NavigationStateChart.h b/src/Gui/Navigation/NavigationStateChart.h index f7e7a50008..9e60708d1a 100644 --- a/src/Gui/Navigation/NavigationStateChart.h +++ b/src/Gui/Navigation/NavigationStateChart.h @@ -32,6 +32,8 @@ namespace Gui { +class NaviStateMachine; + class GuiExport NavigationStateChart : public UserNavigationStyle { using inherited = UserNavigationStyle; @@ -89,57 +91,49 @@ public: std::shared_ptr flags; }; - class StateMachine - { - public: - template - void make_object(T* obj) - { - object = std::make_shared>(obj); - } - - void process_event(const Event& ev) - { - if (object) { - object->process_event(ev); - } - } - - struct Concept - { - virtual ~Concept() = default; - virtual void process_event(const Event&) = 0; - }; - - template< typename T > - struct Model : Concept - { - explicit Model(T* t) : object(t) - { - object->initiate(); - } - ~Model() override - { - object.reset(); - } - void process_event(const Event& ev) override - { - object->process_event(ev); - } - - private: - std::shared_ptr object; - }; - - std::shared_ptr object; - }; - NavigationStateChart(); ~NavigationStateChart() override; protected: SbBool processSoEvent(const SoEvent * const ev) override; - std::unique_ptr naviMachine; // NOLINT + std::unique_ptr naviMachine; // NOLINT +}; + +class GuiExport NaviStateMachine +{ +public: + NaviStateMachine(const NaviStateMachine&) = delete; + NaviStateMachine(NaviStateMachine&&) = delete; + NaviStateMachine& operator= (const NaviStateMachine&) = delete; + NaviStateMachine& operator= (NaviStateMachine&&) = delete; + + NaviStateMachine() = default; + virtual ~NaviStateMachine() = default; + + virtual void process_event(const NavigationStateChart::Event&) = 0; +}; + +template< typename T > +class NaviStateMachineT : public NaviStateMachine +{ +public: + explicit NaviStateMachineT(T* t) : object(t) + { + object->initiate(); + } + + ~NaviStateMachineT() override + { + object.reset(); + } + + void process_event(const NavigationStateChart::Event& ev) override + { + object->process_event(ev); + } + +private: + std::unique_ptr object; }; } // namespace Gui diff --git a/src/Gui/Navigation/SiemensNXStyle.cpp b/src/Gui/Navigation/SiemensNXStyle.cpp index 98721dee98..044e5f545c 100644 --- a/src/Gui/Navigation/SiemensNXStyle.cpp +++ b/src/Gui/Navigation/SiemensNXStyle.cpp @@ -402,7 +402,7 @@ TYPESYSTEM_SOURCE(Gui::SiemensNXStyle, Gui::UserNavigationStyle) SiemensNXStyle::SiemensNXStyle() { - naviMachine->make_object(new NS::NaviMachine(*this)); + naviMachine.reset(new NaviStateMachineT(new NaviMachine(*this))); } SiemensNXStyle::~SiemensNXStyle() From fc5b5fd9c6fbe606904acc9ffff8afd8cddf3c1c Mon Sep 17 00:00:00 2001 From: Max Wilfinger Date: Fri, 6 Jun 2025 10:04:29 +0200 Subject: [PATCH 5/6] Added UI for Siemens NX navigation style. Ordered navigation styles alphabetically. --- src/Gui/SoFCDB.cpp | 14 ++-- src/Mod/Tux/NavigationIndicatorGui.py | 82 +++++++++++++++---- src/Mod/Tux/Resources/Tux.qrc | 2 + .../icons/NavigationSiemensNX_dark.svg | 50 +++++++++++ .../icons/NavigationSiemensNX_light.svg | 50 +++++++++++ 5 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 src/Mod/Tux/Resources/icons/NavigationSiemensNX_dark.svg create mode 100644 src/Mod/Tux/Resources/icons/NavigationSiemensNX_light.svg diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index ab6f365df4..7a0dc4718c 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -190,18 +190,18 @@ void Gui::SoFCDB::init() NavigationStyle ::init(); UserNavigationStyle ::init(); NavigationStateChart ::init(); - InventorNavigationStyle ::init(); - CADNavigationStyle ::init(); - RevitNavigationStyle ::init(); BlenderNavigationStyle ::init(); - SolidWorksNavigationStyle ::init(); - MayaGestureNavigationStyle ::init(); - TouchpadNavigationStyle ::init(); + CADNavigationStyle ::init(); GestureNavigationStyle ::init(); + MayaGestureNavigationStyle ::init(); OpenCascadeNavigationStyle ::init(); + InventorNavigationStyle ::init(); OpenSCADNavigationStyle ::init(); - TinkerCADNavigationStyle ::init(); + RevitNavigationStyle ::init(); SiemensNXStyle ::init(); + SolidWorksNavigationStyle ::init(); + TinkerCADNavigationStyle ::init(); + TouchpadNavigationStyle ::init(); GLGraphicsItem ::init(); GLFlagWindow ::init(); diff --git a/src/Mod/Tux/NavigationIndicatorGui.py b/src/Mod/Tux/NavigationIndicatorGui.py index 84b7f3db7b..3e68c64a90 100644 --- a/src/Mod/Tux/NavigationIndicatorGui.py +++ b/src/Mod/Tux/NavigationIndicatorGui.py @@ -74,9 +74,10 @@ def RePopulateIcons(): a6.setIcon(QtGui.QIcon(":/icons/NavigationOpenInventor_" + StyleSheetType + ".svg")) a7.setIcon(QtGui.QIcon(":/icons/NavigationOpenSCAD_" + StyleSheetType + ".svg")) a8.setIcon(QtGui.QIcon(":/icons/NavigationRevit_" + StyleSheetType + ".svg")) - a9.setIcon(QtGui.QIcon(":/icons/NavigationSolidWorks_" + StyleSheetType + ".svg")) - a10.setIcon(QtGui.QIcon(":/icons/NavigationTinkerCAD_" + StyleSheetType + ".svg")) - a11.setIcon(QtGui.QIcon(":/icons/NavigationTouchpad_" + StyleSheetType + ".svg")) + a9.setIcon(QtGui.QIcon(":/icons/NavigationSiemensNX_" + StyleSheetType + ".svg")) + a10.setIcon(QtGui.QIcon(":/icons/NavigationSolidWorks_" + StyleSheetType + ".svg")) + a11.setIcon(QtGui.QIcon(":/icons/NavigationTinkerCAD_" + StyleSheetType + ".svg")) + a12.setIcon(QtGui.QIcon(":/icons/NavigationTouchpad_" + StyleSheetType + ".svg")) def retranslateUi(): @@ -474,6 +475,48 @@ def retranslateUi(): global t9 t9 = ( + "

Siemens NX " + + text06 + + """

+ + + + + + + + + + + + + + + + + +
""" + + text01 + + """""" + + text02 + + """""" + + text02 + + """""" + + text03 + + """""" + + text04 + + """""" + + text04 + + """
+ """ + + text08 + + ": " + + text10 + + "

" + ) + + global t10 + t10 = ( "

SolidWorks " + text06 + """

@@ -510,8 +553,8 @@ def retranslateUi(): + "

" ) - global t10 - t10 = ( + global t11 + t11 = ( "

TinkerCAD " + text06 + """

@@ -539,8 +582,8 @@ def retranslateUi(): """ ) - global t11 - t11 = ( + global t12 + t12 = ( "

Touchpad " + text06 + """

@@ -716,19 +759,24 @@ a8.setData("Gui::RevitNavigationStyle") a8.setObjectName("Indicator_NavigationRevit") a9 = QtGui.QAction(gStyle) -a9.setText("SolidWorks ") -a9.setData("Gui::SolidWorksNavigationStyle") -a9.setObjectName("Indicator_NavigationSolidWorks") +a9.setText("Siemens NX ") +a9.setData("Gui::SiemensNXStyle") +a9.setObjectName("Indicator_NavigationSiemensNX") a10 = QtGui.QAction(gStyle) -a10.setText("TinkerCAD ") -a10.setData("Gui::TinkerCADNavigationStyle") -a10.setObjectName("Indicator_NavigationTinkerCAD") +a10.setText("SolidWorks ") +a10.setData("Gui::SolidWorksNavigationStyle") +a10.setObjectName("Indicator_NavigationSolidWorks") a11 = QtGui.QAction(gStyle) -a11.setText("Touchpad ") -a11.setData("Gui::TouchpadNavigationStyle") -a11.setObjectName("Indicator_NavigationTouchpad") +a11.setText("TinkerCAD ") +a11.setData("Gui::TinkerCADNavigationStyle") +a11.setObjectName("Indicator_NavigationTinkerCAD") + +a12 = QtGui.QAction(gStyle) +a12.setText("Touchpad ") +a12.setData("Gui::TouchpadNavigationStyle") +a12.setObjectName("Indicator_NavigationTouchpad") RePopulateIcons() @@ -746,6 +794,7 @@ menu.addAction(a8) menu.addAction(a9) menu.addAction(a10) menu.addAction(a11) +menu.addAction(a12) pView.Attach(indicator) @@ -787,6 +836,7 @@ def onTooltip(): a9.setToolTip(t9) a10.setToolTip(t10) a11.setToolTip(t11) + a12.setToolTip(t12) p.SetBool("Tooltip", 1) else: for i in gStyle.actions(): diff --git a/src/Mod/Tux/Resources/Tux.qrc b/src/Mod/Tux/Resources/Tux.qrc index 6ec43834ca..413d549458 100644 --- a/src/Mod/Tux/Resources/Tux.qrc +++ b/src/Mod/Tux/Resources/Tux.qrc @@ -47,6 +47,8 @@ icons/NavigationOpenSCAD_dark.svg icons/NavigationRevit_light.svg icons/NavigationRevit_dark.svg + icons/NavigationSiemensNX_light.svg + icons/NavigationSiemensNX_dark.svg icons/NavigationSolidWorks_light.svg icons/NavigationSolidWorks_dark.svg icons/NavigationTinkerCAD_light.svg diff --git a/src/Mod/Tux/Resources/icons/NavigationSiemensNX_dark.svg b/src/Mod/Tux/Resources/icons/NavigationSiemensNX_dark.svg new file mode 100644 index 0000000000..38ab2c06cc --- /dev/null +++ b/src/Mod/Tux/Resources/icons/NavigationSiemensNX_dark.svg @@ -0,0 +1,50 @@ + + + + + + + + + image/svg+xml + + + + + + + + + NX + diff --git a/src/Mod/Tux/Resources/icons/NavigationSiemensNX_light.svg b/src/Mod/Tux/Resources/icons/NavigationSiemensNX_light.svg new file mode 100644 index 0000000000..0064477a86 --- /dev/null +++ b/src/Mod/Tux/Resources/icons/NavigationSiemensNX_light.svg @@ -0,0 +1,50 @@ + + + + + + + + + image/svg+xml + + + + + + + + + NX + From d9a0b6909a876c996c4aceccd5207369c3ed6b02 Mon Sep 17 00:00:00 2001 From: Max Wilfinger Date: Fri, 6 Jun 2025 15:32:05 +0200 Subject: [PATCH 6/6] Renaming Gui::SiemensNXStyle to Gui::SiemensNXNavigationStyle for consistency --- src/Gui/CMakeLists.txt | 4 ++-- ...XStyle.cpp => SiemensNXNavigationStyle.cpp} | 18 +++++++++--------- ...ensNXStyle.h => SiemensNXNavigationStyle.h} | 12 ++++++------ src/Gui/SoFCDB.cpp | 4 ++-- src/Mod/Tux/NavigationIndicatorGui.py | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) rename src/Gui/Navigation/{SiemensNXStyle.cpp => SiemensNXNavigationStyle.cpp} (96%) rename src/Gui/Navigation/{SiemensNXStyle.h => SiemensNXNavigationStyle.h} (89%) diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 49391e1c2b..ab93d16cac 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -939,7 +939,7 @@ SET(Navigation_CPP_SRCS Navigation/CADNavigationStyle.cpp Navigation/RevitNavigationStyle.cpp Navigation/BlenderNavigationStyle.cpp - Navigation/SiemensNXStyle.cpp + Navigation/SiemensNXNavigationStyle.cpp Navigation/SolidWorksNavigationStyle.cpp Navigation/MayaGestureNavigationStyle.cpp Navigation/OpenCascadeNavigationStyle.cpp @@ -958,7 +958,7 @@ SET(Navigation_SRCS Navigation/GestureNavigationStyle.h Navigation/NavigationAnimator.h Navigation/NavigationAnimation.h - Navigation/SiemensNXStyle.h + Navigation/SiemensNXNavigationStyle.h ) SOURCE_GROUP("Navigation" FILES ${Navigation_SRCS}) diff --git a/src/Gui/Navigation/SiemensNXStyle.cpp b/src/Gui/Navigation/SiemensNXNavigationStyle.cpp similarity index 96% rename from src/Gui/Navigation/SiemensNXStyle.cpp rename to src/Gui/Navigation/SiemensNXNavigationStyle.cpp index 044e5f545c..45090aa2bf 100644 --- a/src/Gui/Navigation/SiemensNXStyle.cpp +++ b/src/Gui/Navigation/SiemensNXNavigationStyle.cpp @@ -32,7 +32,7 @@ #include #include "Camera.h" -#include "SiemensNXStyle.h" +#include "SiemensNXNavigationStyle.h" #include "View3DInventorViewer.h" // NOLINTBEGIN(cppcoreguidelines-pro-type-static-cast-downcast, @@ -41,7 +41,7 @@ using namespace Gui; namespace sc = boost::statechart; using SC = NavigationStateChart; -using NS = SiemensNXStyle; +using NS = SiemensNXNavigationStyle; struct NS::NaviMachine: public sc::state_machine { @@ -396,20 +396,20 @@ struct NS::SelectionState: public sc::state // ---------------------------------------------------------------------------------- -/* TRANSLATOR Gui::SiemensNXStyle */ +/* TRANSLATOR Gui::SiemensNXNavigationStyle */ -TYPESYSTEM_SOURCE(Gui::SiemensNXStyle, Gui::UserNavigationStyle) +TYPESYSTEM_SOURCE(Gui::SiemensNXNavigationStyle, Gui::UserNavigationStyle) -SiemensNXStyle::SiemensNXStyle() +SiemensNXNavigationStyle::SiemensNXNavigationStyle() { naviMachine.reset(new NaviStateMachineT(new NaviMachine(*this))); } -SiemensNXStyle::~SiemensNXStyle() +SiemensNXNavigationStyle::~SiemensNXNavigationStyle() { } -const char* SiemensNXStyle::mouseButtons(ViewerMode mode) +const char* SiemensNXNavigationStyle::mouseButtons(ViewerMode mode) { switch (mode) { case NavigationStyle::SELECTION: @@ -425,12 +425,12 @@ const char* SiemensNXStyle::mouseButtons(ViewerMode mode) } } -std::string SiemensNXStyle::userFriendlyName() const +std::string SiemensNXNavigationStyle::userFriendlyName() const { return {"Siemens NX"}; } -SbBool SiemensNXStyle::processKeyboardEvent(const SoKeyboardEvent * const event) +SbBool SiemensNXNavigationStyle::processKeyboardEvent(const SoKeyboardEvent* const event) { // See https://forum.freecad.org/viewtopic.php?t=96459 // Isometric view: Home key button diff --git a/src/Gui/Navigation/SiemensNXStyle.h b/src/Gui/Navigation/SiemensNXNavigationStyle.h similarity index 89% rename from src/Gui/Navigation/SiemensNXStyle.h rename to src/Gui/Navigation/SiemensNXNavigationStyle.h index efd36afadc..a888551df0 100644 --- a/src/Gui/Navigation/SiemensNXStyle.h +++ b/src/Gui/Navigation/SiemensNXNavigationStyle.h @@ -22,8 +22,8 @@ **************************************************************************/ -#ifndef GUI_SIEMENSNXSTYLE_H -#define GUI_SIEMENSNXSTYLE_H +#ifndef GUI_SIEMENSNXNAVIGATIONSTYLE_H +#define GUI_SIEMENSNXNAVIGATIONSTYLE_H #include @@ -31,14 +31,14 @@ namespace Gui { -class GuiExport SiemensNXStyle : public NavigationStateChart { +class GuiExport SiemensNXNavigationStyle : public NavigationStateChart { using inherited = NavigationStateChart; TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - SiemensNXStyle(); - ~SiemensNXStyle() override; + SiemensNXNavigationStyle(); + ~SiemensNXNavigationStyle() override; const char* mouseButtons(ViewerMode mode) override; std::string userFriendlyName() const override; @@ -60,4 +60,4 @@ private: } // namespace Gui // NOLINTEND(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls) -#endif // GUI_SIEMENSNXSTYLE_H +#endif // GUI_SIEMENSNXNAVIGATIONSTYLE_H diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 7a0dc4718c..19b32fda94 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -53,7 +53,7 @@ #include "Inventor/Draggers/SoTransformDragger.h" #include "Navigation/GestureNavigationStyle.h" #include "Navigation/NavigationStyle.h" -#include "Navigation/SiemensNXStyle.h" +#include "Navigation/SiemensNXNavigationStyle.h" #include "SelectionObject.h" #include "SoDevicePixelRatioElement.h" #include "SoFCColorBar.h" @@ -198,7 +198,7 @@ void Gui::SoFCDB::init() InventorNavigationStyle ::init(); OpenSCADNavigationStyle ::init(); RevitNavigationStyle ::init(); - SiemensNXStyle ::init(); + SiemensNXNavigationStyle ::init(); SolidWorksNavigationStyle ::init(); TinkerCADNavigationStyle ::init(); TouchpadNavigationStyle ::init(); diff --git a/src/Mod/Tux/NavigationIndicatorGui.py b/src/Mod/Tux/NavigationIndicatorGui.py index 3e68c64a90..194ead53ab 100644 --- a/src/Mod/Tux/NavigationIndicatorGui.py +++ b/src/Mod/Tux/NavigationIndicatorGui.py @@ -760,7 +760,7 @@ a8.setObjectName("Indicator_NavigationRevit") a9 = QtGui.QAction(gStyle) a9.setText("Siemens NX ") -a9.setData("Gui::SiemensNXStyle") +a9.setData("Gui::SiemensNXNavigationStyle") a9.setObjectName("Indicator_NavigationSiemensNX") a10 = QtGui.QAction(gStyle)