Gui: Refactor navigation styles
Add new base class for state chart based navigation styles
This commit is contained in:
@@ -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
|
||||
|
||||
233
src/Gui/Navigation/NavigationStateChart.cpp
Normal file
233
src/Gui/Navigation/NavigationStateChart.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#include <boost/statechart/custom_reaction.hpp>
|
||||
#include <boost/statechart/state_machine.hpp>
|
||||
#include <boost/statechart/state.hpp>
|
||||
|
||||
#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<const SoMouseButtonEvent*>(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<const SoKeyboardEvent*>(this->inventor_event);
|
||||
}
|
||||
|
||||
bool NS::Event::isLocation2Event() const
|
||||
{
|
||||
return this->inventor_event->isOfType(SoLocation2Event::getClassTypeId());
|
||||
}
|
||||
|
||||
const SoLocation2Event* NS::Event::asLocation2Event() const
|
||||
{
|
||||
return static_cast<const SoLocation2Event*>(this->inventor_event);
|
||||
}
|
||||
|
||||
bool NS::Event::isMotion3Event() const
|
||||
{
|
||||
return this->inventor_event->isOfType(SoMotion3Event::getClassTypeId());
|
||||
}
|
||||
|
||||
const SoMotion3Event* NS::Event::asMotion3Event() const
|
||||
{
|
||||
return static_cast<const SoMotion3Event*>(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<const SoMotion3Event*>(ev));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keyboard handling
|
||||
if (ev->isOfType(SoKeyboardEvent::getClassTypeId())) {
|
||||
const auto event = static_cast<const SoKeyboardEvent *>(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;
|
||||
}
|
||||
148
src/Gui/Navigation/NavigationStateChart.h
Normal file
148
src/Gui/Navigation/NavigationStateChart.h
Normal file
@@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2025 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* 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 *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#ifndef GUI_NAVIGATIONSTATECHART_H
|
||||
#define GUI_NAVIGATIONSTATECHART_H
|
||||
|
||||
#include <boost/statechart/event.hpp>
|
||||
#include <Gui/Navigation/NavigationStyle.h>
|
||||
|
||||
// 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<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> flags;
|
||||
};
|
||||
|
||||
class StateMachine
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
void make_object(T* obj)
|
||||
{
|
||||
object = std::make_shared<Model<T>>(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<T> object;
|
||||
};
|
||||
|
||||
std::shared_ptr<Concept> object;
|
||||
};
|
||||
|
||||
NavigationStateChart();
|
||||
~NavigationStateChart() override;
|
||||
|
||||
protected:
|
||||
SbBool processSoEvent(const SoEvent * const ev) override;
|
||||
std::unique_ptr<StateMachine> naviMachine; // NOLINT
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
// NOLINTEND(cppcoreguidelines-avoid*, readability-avoid-const-params-in-decls)
|
||||
|
||||
#endif // GUI_NAVIGATIONSTATECHART_H
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user