first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

15
src/Canvas/CMakeLists.txt Normal file
View File

@@ -0,0 +1,15 @@
include(FlightGearComponent)
set(SOURCES
canvas_mgr.cxx
FGCanvasSystemAdapter.cxx
gui_mgr.cxx
)
set(HEADERS
canvas_mgr.hxx
FGCanvasSystemAdapter.hxx
gui_mgr.hxx
)
flightgear_component(Canvas "${SOURCES}" "${HEADERS}")

View File

@@ -0,0 +1,129 @@
// Integrate Canvas into FlightGear
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "FGCanvasSystemAdapter.hxx"
#include <Main/globals.hxx>
#include <Network/HTTPClient.hxx>
#include <Viewer/renderer.hxx>
#include <osg/Version>
#include <osgDB/ReadFile>
#include <stdexcept>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
namespace canvas
{
//----------------------------------------------------------------------------
simgear::canvas::FontPtr
FGCanvasSystemAdapter::getFont(const std::string& name) const
{
SGPath path = globals->resolve_resource_path("Fonts/" + name);
if( path.isNull() )
{
SG_LOG
(
SG_GL,
SG_ALERT,
"canvas::Text: No such font: " << name
);
return simgear::canvas::FontPtr();
}
SG_LOG
(
SG_GL,
SG_DEBUG,
"canvas::Text: using font file " << path
);
simgear::canvas::FontPtr font = osgText::readFontFile(path.utf8Str());
if( !font )
SG_LOG
(
SG_GL,
SG_ALERT,
"canvas::Text: Failed to open font file " << path
);
return font;
}
//----------------------------------------------------------------------------
void FGCanvasSystemAdapter::addCamera(osg::Camera* camera) const
{
globals->get_renderer()->addCamera(camera, false);
}
//----------------------------------------------------------------------------
void FGCanvasSystemAdapter::removeCamera(osg::Camera* camera) const
{
if( globals->get_renderer() )
globals->get_renderer()->removeCamera(camera);
}
//----------------------------------------------------------------------------
osg::ref_ptr<osg::Image> FGCanvasSystemAdapter::getImage(const std::string& path) const
{
SGPath p(SGPath::fromUtf8(path));
osg::ref_ptr<simgear::SGReaderWriterOptions> localReaderWriterOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions());
localReaderWriterOptions->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_CANVAS);
if( p.isAbsolute() )
{
const SGPath valid_path = SGPath(p).validate(false);
if( !valid_path.isNull() )
return osgDB::readRefImageFile(valid_path.utf8Str(), localReaderWriterOptions);
SG_LOG(SG_IO, SG_ALERT, "canvas::Image: reading '" << path << "' denied");
}
else
{
SGPath tpath = globals->resolve_resource_path(path);
if( !tpath.isNull() )
return osgDB::readRefImageFile(tpath.utf8Str(), localReaderWriterOptions);
SG_LOG(SG_IO, SG_ALERT, "canvas::Image: No such image: '" << path << "'");
}
return 0;
}
//----------------------------------------------------------------------------
SGSubsystem*
FGCanvasSystemAdapter::getSubsystem(const std::string& name) const
{
return globals->get_subsystem(name.c_str());
}
//----------------------------------------------------------------------------
simgear::HTTP::Client* FGCanvasSystemAdapter::getHTTPClient() const
{
FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>();
if( http )
return http->client();
SG_LOG( SG_IO,
SG_ALERT,
"FGCanvasSystemAdapter: Failed to get HTTP subsystem" );
return nullptr;
}
}

View File

@@ -0,0 +1,39 @@
// Integrate Canvas into FlightGear
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef FG_CANVASSYSTEMADAPTER_HXX_
#define FG_CANVASSYSTEMADAPTER_HXX_
#include <simgear/canvas/CanvasSystemAdapter.hxx>
namespace canvas
{
class FGCanvasSystemAdapter:
public simgear::canvas::SystemAdapter
{
public:
simgear::canvas::FontPtr getFont(const std::string& name) const override;
void addCamera(osg::Camera* camera) const override;
void removeCamera(osg::Camera* camera) const override;
osg::ref_ptr<osg::Image> getImage(const std::string& path) const override;
SGSubsystem* getSubsystem(const std::string& name) const override;
simgear::HTTP::Client* getHTTPClient() const override;
};
}
#endif /* FG_CANVASSYSTEMADAPTER_HXX_ */

172
src/Canvas/canvas_mgr.cxx Normal file
View File

@@ -0,0 +1,172 @@
// Canvas with 2D rendering api
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "canvas_mgr.hxx"
#include <Cockpit/od_gauge.hxx>
#include <Main/fg_props.hxx>
#include <Scripting/NasalModelData.hxx>
#include <Viewer/CameraGroup.hxx>
#include <Model/modelmgr.hxx>
#include <simgear/canvas/Canvas.hxx>
#include <simgear/scene/model/placement.hxx>
namespace sc = simgear::canvas;
//------------------------------------------------------------------------------
static sc::Placements addSceneObjectPlacement( SGPropertyNode* placement,
sc::CanvasPtr canvas )
{
int module_id = placement->getIntValue("module-id", -1);
if( module_id < 0 )
return sc::Placements();
FGNasalModelData* model_data =
FGNasalModelData::getByModuleId( static_cast<unsigned int>(module_id) );
if( !model_data )
return sc::Placements();
if( !model_data->getNode() )
return sc::Placements();
return FGODGauge::set_texture
(
model_data->getNode(),
placement,
canvas->getTexture(),
canvas->getCullCallback(),
canvas
);
}
//------------------------------------------------------------------------------
static sc::Placements addDynamicModelPlacement(SGPropertyNode* placement,
sc::CanvasPtr canvas)
{
const string dyn_model_path = placement->getStringValue("model-path");
if (dyn_model_path.empty())
return {};
auto modelmgr = globals->get_subsystem<FGModelMgr>();
if (!modelmgr)
return {};
FGModelMgr::Instance* model_instance = modelmgr->findInstanceByNodePath(dyn_model_path);
if (!model_instance || !model_instance->model || !model_instance->model->getSceneGraph())
return {};
return FGODGauge::set_texture(
model_instance->model->getSceneGraph(),
placement,
canvas->getTexture(),
canvas->getCullCallback(),
canvas);
}
//------------------------------------------------------------------------------
CanvasMgr::CanvasMgr():
simgear::canvas::CanvasMgr( fgGetNode("/canvas/by-index", true) ),
_cb_model_reinit
(
this,
&CanvasMgr::handleModelReinit,
fgGetNode("/sim/signals/model-reinit", true)
)
{
}
//----------------------------------------------------------------------------
void CanvasMgr::init()
{
// add our two placement factories
sc::Canvas::addPlacementFactory
(
"object", [](SGPropertyNode* placement, sc::CanvasPtr canvas) {
return FGODGauge::set_aircraft_texture(placement,
canvas->getTexture(),
canvas->getCullCallback(),
canvas);
});
sc::Canvas::addPlacementFactory("scenery-object", &addSceneObjectPlacement);
sc::Canvas::addPlacementFactory("dynamic-model", &addDynamicModelPlacement);
simgear::canvas::CanvasMgr::init();
}
//----------------------------------------------------------------------------
void CanvasMgr::shutdown()
{
simgear::canvas::CanvasMgr::shutdown();
sc::Canvas::removePlacementFactory("object");
sc::Canvas::removePlacementFactory("scenery-object");
sc::Canvas::removePlacementFactory("dynamic-model");
}
//------------------------------------------------------------------------------
unsigned int
CanvasMgr::getCanvasTexId(const simgear::canvas::CanvasPtr& canvas) const
{
if( !canvas )
return 0;
osg::Texture2D* tex = canvas->getTexture();
if( !tex )
return 0;
// osgViewer::Viewer::Contexts contexts;
// globals->get_renderer()->getViewer()->getContexts(contexts);
//
// if( contexts.empty() )
// return 0;
osg::Camera* guiCamera = flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
if (!guiCamera)
return 0;
osg::State* state = guiCamera->getGraphicsContext()->getState(); //contexts[0]->getState();
if( !state )
return 0;
osg::Texture::TextureObject* tobj =
tex->getTextureObject( state->getContextID() );
if( !tobj )
return 0;
return tobj->_id;
}
//----------------------------------------------------------------------------
void CanvasMgr::handleModelReinit(SGPropertyNode*)
{
for (size_t i = 0; i < _elements.size(); ++i)
{
sc::Canvas* element = static_cast<sc::Canvas*>(_elements[i].get());
if (element)
element->reloadPlacements("object");
}
}
// Register the subsystem.
SGSubsystemMgr::Registrant<CanvasMgr> registrantCanvasMgr(
SGSubsystemMgr::DISPLAY);

55
src/Canvas/canvas_mgr.hxx Normal file
View File

@@ -0,0 +1,55 @@
// Canvas with 2D rendering api
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef CANVAS_MGR_H_
#define CANVAS_MGR_H_
#include <simgear/canvas/CanvasMgr.hxx>
#include <simgear/props/PropertyBasedMgr.hxx>
class CanvasMgr : public simgear::canvas::CanvasMgr
{
public:
CanvasMgr();
// Subsystem API.
void init() override;
void shutdown() override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "Canvas"; }
/**
* Get OpenGL texture name for given canvas
*
* @deprecated This was only meant to be used by the PUI CanvasWidget
* implementation as PUI can't handle osg::Texture objects.
* Use getCanvas(index)->getTexture() instead.
*
* @return OpenGL texture name
*/
unsigned int getCanvasTexId(const simgear::canvas::CanvasPtr& canvas) const;
protected:
SGPropertyChangeCallback<CanvasMgr> _cb_model_reinit;
void handleModelReinit(SGPropertyNode*);
};
#endif /* CANVAS_MGR_H_ */

780
src/Canvas/gui_mgr.cxx Normal file
View File

@@ -0,0 +1,780 @@
// Canvas gui/dialog manager
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gui_mgr.hxx"
#include <Main/fg_os.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasPlacement.hxx>
#include <simgear/canvas/CanvasWindow.hxx>
#include <simgear/canvas/events/KeyboardEvent.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <osg/BlendFunc>
#include <osgViewer/Viewer>
#include <osgViewer/View>
#include <osgGA/GUIEventHandler>
class DesktopGroup;
typedef SGSharedPtr<DesktopGroup> DesktopPtr;
typedef SGWeakPtr<DesktopGroup> DesktopWeakPtr;
namespace sc = simgear::canvas;
using osgEA = osgGA::GUIEventAdapter;
/*
RESIZE AREAS
============
| || | _ inside corner region (L-shaped part inside margin) both
|___||_|_ _ _/ directions can be resized (outside only one axis)
| || | |
| || |
| || |_____|__ _
| || | } margin_neg \
| ========|== <-- window border |_ area where resize
| | } margin_pos | can be initiated
|____________|__/ _/
|<- corner ->|
*/
const float RESIZE_MARGIN_POS = 12;
const float RESIZE_MARGIN_NEG = 2;
const float RESIZE_CORNER = 20;
/**
* Event handler
*/
class GUIEventHandler:
public osgGA::GUIEventHandler
{
public:
GUIEventHandler(const DesktopWeakPtr& desktop_group);
bool handle( const osgEA& ea,
osgGA::GUIActionAdapter&,
osg::Object*,
osg::NodeVisitor* );
protected:
DesktopWeakPtr _desktop;
};
/**
* Track a canvas placement on a window
*/
class WindowPlacement:
public sc::Placement
{
public:
WindowPlacement( SGPropertyNode* node,
sc::WindowPtr window,
sc::CanvasPtr canvas ):
Placement(node),
_window(window),
_canvas(canvas)
{}
/**
* Remove placement from window
*/
virtual ~WindowPlacement()
{
sc::WindowPtr window = _window.lock();
sc::CanvasPtr canvas = _canvas.lock();
if( window && canvas && canvas == window->getCanvasContent().lock() )
window->setCanvasContent( sc::CanvasPtr() );
}
private:
sc::WindowWeakPtr _window;
sc::CanvasWeakPtr _canvas;
};
/**
* Desktop root group
*/
class DesktopGroup:
public sc::Group
{
public:
DesktopGroup(osg::Camera* camera);
void setFocusWindow(const sc::WindowPtr& window);
bool grabPointer(const sc::WindowPtr& window);
void ungrabPointer(const sc::WindowPtr& window);
sc::WindowPtr windowAtPosition(const osg::Vec2f& screen_pos);
osg::Vec2f toScreenPos(const osgEA& ea) const;
bool handleOsgEvent(const osgEA& ea);
protected:
friend class GUIMgr;
SGPropertyChangeCallback<DesktopGroup> _cb_mouse_mode;
bool _handle_events {true};
simgear::PropertyObject<int> _width,
_height;
sc::WindowWeakPtr _last_push,
_last_drag,
_last_mouse_over,
_resize_window,
_focus_window,
_pointer_grab_window;
uint8_t _resize {sc::Window::NONE};
int _last_cursor {MOUSE_CURSOR_NONE};
bool _drag_finished {false};
osg::Vec2f _drag_start,
_last_mouse_pos;
double _last_scroll_time {0};
uint32_t _last_key_down_no_mod {~0u}; // Key repeat for non modifier keys
bool canHandleInput() const;
bool handleMouse(const osgEA& ea);
bool handleKeyboard(const osgEA& ea);
bool propagateEvent( const sc::EventPtr& event,
const sc::WindowPtr& active_window );
bool propagateRootEvent(const sc::EventPtr& event);
void handleResize(int x, int y, int width, int height);
bool handleDrag(const sc::EventPtr& event);
void finishDrag(const sc::WindowPtr& drag_src, const sc::EventPtr& event);
void handleMouseMode(SGPropertyNode* node);
/**
*
*/
sc::ElementFactory
getChildFactory(const std::string& type) const
{
if( type == "window" )
return &Element::create<sc::Window>;
return Group::getChildFactory(type);
}
};
//------------------------------------------------------------------------------
GUIEventHandler::GUIEventHandler(const DesktopWeakPtr& desktop_group):
_desktop( desktop_group )
{
}
//------------------------------------------------------------------------------
bool GUIEventHandler::handle( const osgEA& ea,
osgGA::GUIActionAdapter&,
osg::Object*,
osg::NodeVisitor* )
{
if( ea.getHandled() )
return false;
DesktopPtr desktop = _desktop.lock();
return desktop && desktop->handleOsgEvent(ea);
}
//------------------------------------------------------------------------------
DesktopGroup::DesktopGroup(osg::Camera* camera):
Group(sc::CanvasPtr(), fgGetNode("/sim/gui/canvas", true)),
_cb_mouse_mode( this,
&DesktopGroup::handleMouseMode,
fgGetNode("/devices/status/mice/mouse[0]/mode") ),
_width(_node, "size[0]"),
_height(_node, "size[1]")
{
if( !camera )
{
SG_LOG(SG_GUI, SG_WARN, "DesktopGroup: failed to get GUI camera.");
return;
}
camera->addChild(_scene_group.get());
osg::StateSet* stateSet = _scene_group->getOrCreateStateSet();
stateSet->setDataVariance(osg::Object::STATIC);
stateSet->setRenderBinDetails(1000, "RenderBin");
// speed optimization?
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
stateSet->setAttribute(new osg::BlendFunc(
osg::BlendFunc::SRC_ALPHA,
osg::BlendFunc::ONE_MINUS_SRC_ALPHA)
);
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
_width = _height = -1;
}
//------------------------------------------------------------------------------
void DesktopGroup::setFocusWindow(const sc::WindowPtr& window)
{
_focus_window = window;
}
//------------------------------------------------------------------------------
bool DesktopGroup::grabPointer(const sc::WindowPtr& window)
{
sc::WindowPtr resize = _resize_window.lock();
if( (resize && resize != window) || !_pointer_grab_window.expired() )
// Already grabbed (resize -> implicit grab)
return false;
_pointer_grab_window = window;
return true;
}
//------------------------------------------------------------------------------
void DesktopGroup::ungrabPointer(const sc::WindowPtr& window)
{
if( _pointer_grab_window.expired() )
SG_LOG(SG_GUI, SG_WARN, "ungrabPointer: no active grab.");
else if( window != _pointer_grab_window.lock() )
SG_LOG(SG_GUI, SG_WARN, "ungrabPointer: window is not owner of the grab.");
else
_pointer_grab_window.reset();
}
//------------------------------------------------------------------------------
sc::WindowPtr DesktopGroup::windowAtPosition(const osg::Vec2f& screen_pos)
{
for( int i = _scene_group->getNumChildren() - 1; i >= 0; --i )
{
osg::Group *element = _scene_group->getChild(i)->asGroup();
if( !element || !element->getUserData() )
continue; // TODO warn/log?
sc::WindowPtr window =
dynamic_cast<sc::Window*>
(
static_cast<sc::Element::OSGUserData*>(
element->getUserData()
)->element.get()
);
if( !window || !window->isCapturingEvents() || !window->isVisible() )
continue;
float margin = window->isResizable() ? RESIZE_MARGIN_POS : 0;
if( window->getScreenRegion().contains( screen_pos.x(),
screen_pos.y(),
margin ) )
{
return window;
}
}
return {};
}
//------------------------------------------------------------------------------
osg::Vec2f DesktopGroup::toScreenPos(const osgEA& ea) const
{
float x = SGMiscf::round(0.5 * (ea.getXnormalized() + 1) * _width);
float y = SGMiscf::round(0.5 * (ea.getYnormalized() + 1) * _height);
if( ea.getMouseYOrientation() != osgEA::Y_INCREASING_DOWNWARDS )
y = _height - y;
return {x, y};
}
//------------------------------------------------------------------------------
bool DesktopGroup::handleOsgEvent(const osgEA& ea)
{
switch( ea.getEventType() )
{
case osgEA::PUSH:
case osgEA::RELEASE:
// case osgEA::DOUBLECLICK:
// // DOUBLECLICK doesn't seem to be triggered...
case osgEA::DRAG:
case osgEA::MOVE:
case osgEA::SCROLL:
return handleMouse(ea);
case osgEA::KEYDOWN:
case osgEA::KEYUP:
return handleKeyboard(ea);
case osgEA::RESIZE:
handleResize( ea.getWindowX(),
ea.getWindowY(),
ea.getWindowWidth(),
ea.getWindowHeight() );
return false; // Let other event handlers also consume resize events
default:
return false;
}
}
//------------------------------------------------------------------------------
bool DesktopGroup::canHandleInput() const
{
return _handle_events
&& _scene_group.valid()
&& _scene_group->getNumChildren() > 0;
}
//------------------------------------------------------------------------------
bool DesktopGroup::handleMouse(const osgEA& ea)
{
if( !canHandleInput() )
return false;
osg::Vec2f mouse_pos = toScreenPos(ea),
delta = mouse_pos - _last_mouse_pos;
_last_mouse_pos = mouse_pos;
if( auto resize_window = _resize_window.lock() )
{
switch( ea.getEventType() )
{
case osgEA::RELEASE:
resize_window->handleResize(sc::Window::NONE);
_resize_window.reset();
break;
case osgEA::DRAG:
resize_window->handleResize(_resize, mouse_pos - _drag_start);
return true;
default:
// Ignore all other events while resizing
return true;
}
}
sc::MouseEventPtr event(new sc::MouseEvent(ea));
event->screen_pos = mouse_pos;
event->delta = delta;
if( !_drag_finished && ea.getEventType() == osgEA::DRAG )
return handleDrag(event);
if( auto last_drag = _last_drag.lock() )
{
if( ea.getEventType() == osgEA::RELEASE )
finishDrag(last_drag, event);
else
// While dragging ignore all other mouse events
return true;
}
sc::WindowPtr window_at_cursor = _pointer_grab_window.lock();
if( !window_at_cursor )
window_at_cursor = windowAtPosition(event->screen_pos);
if( window_at_cursor )
{
const SGRect<float>& reg = window_at_cursor->getScreenRegion();
if( window_at_cursor->isResizable()
&& !reg.contains( event->getScreenX(),
event->getScreenY(),
-RESIZE_MARGIN_NEG ) )
{
if( !_last_cursor )
_last_cursor = fgGetMouseCursor();
_resize = 0;
if( event->getScreenX() <= reg.l() + RESIZE_CORNER )
_resize |= sc::Window::LEFT;
else if( event->getScreenX() >= reg.r() - RESIZE_CORNER )
_resize |= sc::Window::RIGHT;
if( event->getScreenY() <= reg.t() + RESIZE_CORNER )
_resize |= sc::Window::TOP;
else if( event->getScreenY() >= reg.b() - RESIZE_CORNER )
_resize |= sc::Window::BOTTOM;
static const int cursor_mapping[] =
{
0, MOUSE_CURSOR_LEFTSIDE, MOUSE_CURSOR_RIGHTSIDE, 0,
MOUSE_CURSOR_TOPSIDE, MOUSE_CURSOR_TOPLEFT, MOUSE_CURSOR_TOPRIGHT, 0,
MOUSE_CURSOR_BOTTOMSIDE, MOUSE_CURSOR_BOTTOMLEFT, MOUSE_CURSOR_BOTTOMRIGHT,
};
if( !cursor_mapping[_resize] )
return false;
fgSetMouseCursor(cursor_mapping[_resize]);
if( ea.getEventType() == osgEA::PUSH )
{
_resize_window = window_at_cursor;
_drag_start = event->screen_pos;
window_at_cursor->raise();
window_at_cursor->handleResize(_resize | sc::Window::INIT);
}
return true;
}
}
if( _last_cursor )
{
fgSetMouseCursor(_last_cursor);
_last_cursor = 0;
return true;
}
switch( ea.getEventType() )
{
case osgEA::PUSH:
_last_push = window_at_cursor;
_drag_finished = false;
event->type = sc::Event::MOUSE_DOWN;
break;
case osgEA::SCROLL:
switch( ea.getScrollingMotion() )
{
case osgEA::SCROLL_UP:
event->delta.y() = 1;
break;
case osgEA::SCROLL_DOWN:
event->delta.y() = -1;
break;
default:
return false;
}
// osg sends two events for every scrolling motion. We don't need
// duplicate events, so lets ignore the second event with the same
// timestamp.
if( _last_scroll_time == ea.getTime() )
return window_at_cursor ? true : false;
_last_scroll_time = ea.getTime();
event->type = sc::Event::WHEEL;
break;
// If drag has not been handled yet it has been aborted. So let's treat it
// like a normal mouse movement.
case osgEA::DRAG:
case osgEA::MOVE:
{
sc::WindowPtr last_mouse_over = _last_mouse_over.lock();
if( last_mouse_over && last_mouse_over != window_at_cursor )
last_mouse_over->handleEvent(event->clone(sc::Event::MOUSE_LEAVE));
_last_mouse_over = window_at_cursor;
event->type = sc::Event::MOUSE_MOVE;
break;
}
case osgEA::RELEASE:
{
sc::WindowPtr last_push = _last_push.lock();
if( last_push && last_push != window_at_cursor )
{
// Leave old window
last_push->handleEvent(event->clone(sc::Event::MOUSE_LEAVE));
}
_last_push.reset();
event->type = sc::Event::MOUSE_UP;
break;
}
default:
return false;
}
return propagateEvent(event, window_at_cursor);
}
//------------------------------------------------------------------------------
bool DesktopGroup::handleKeyboard(const osgEA& ea)
{
if( !canHandleInput() )
return false;
sc::KeyboardEventPtr event(new sc::KeyboardEvent(ea));
if( auto drag = _last_drag.lock() )
{
if( ea.getKey() == osgEA::KEY_Escape )
finishDrag(drag, event);
// While dragging ignore all key events
return true;
}
// Detect key repeat (of non modifier keys)
if( !event->isModifier() )
{
if( event->getType() == sc::Event::KEY_DOWN )
{
if( event->keyCode() == _last_key_down_no_mod )
event->setRepeat(true);
_last_key_down_no_mod = event->keyCode();
}
else
{
if( event->keyCode() == _last_key_down_no_mod )
_last_key_down_no_mod = ~0u;
}
}
sc::WindowPtr active_window = _focus_window.lock();
bool handled = propagateEvent(event, active_window);
if( event->getType() == sc::Event::KEY_DOWN
&& !event->defaultPrevented()
&& event->isPrint() )
{
handled |= propagateEvent(event->clone(sc::Event::KEY_PRESS), active_window);
}
return handled;
}
//------------------------------------------------------------------------------
bool DesktopGroup::propagateEvent( const sc::EventPtr& event,
const sc::WindowPtr& active_window )
{
return active_window
? active_window->handleEvent(event)
: propagateRootEvent(event);
}
//------------------------------------------------------------------------------
bool DesktopGroup::propagateRootEvent(const sc::EventPtr& event)
{
handleEvent(event);
// stopPropagation() on DesktopGroup stops propagation to internal event
// handling.
return event->propagation_stopped;
}
//------------------------------------------------------------------------------
void DesktopGroup::handleResize(int x, int y, int width, int height)
{
if( _width == width && _height == height )
return;
_width = width;
_height = height;
if( _scene_group.valid() )
{
// Origin should be at top left corner, therefore we need to mirror the
// y-axis
_scene_group->setMatrix(osg::Matrix(
1, 0, 0, 0,
0, -1, 0, 0,
0, 0, 1, 0,
0, _height, 0, 1
));
}
}
//------------------------------------------------------------------------------
bool DesktopGroup::handleDrag(const sc::EventPtr& event)
{
event->type = sc::Event::DRAG;
auto drag_window = _last_drag.lock();
if( !drag_window )
{
_last_drag = drag_window = _last_push.lock();
if( drag_window )
drag_window->handleEvent(event->clone(sc::Event::DRAG_START));
}
// TODO dragover
return drag_window && drag_window->handleEvent(event);
}
//------------------------------------------------------------------------------
void DesktopGroup::finishDrag( const sc::WindowPtr& drag_src,
const sc::EventPtr& event )
{
drag_src->handleEvent(event->clone(sc::Event::DRAG_END));
_last_drag.reset();
_drag_finished = true;
}
//------------------------------------------------------------------------------
void DesktopGroup::handleMouseMode(SGPropertyNode* node)
{
// pass-through indicates events should pass through to the UI
_handle_events = fgGetNode("/input/mice/mouse[0]/mode", node->getIntValue())
->getBoolValue("pass-through");
}
//------------------------------------------------------------------------------
GUIMgr::GUIMgr()
{
}
//------------------------------------------------------------------------------
void GUIMgr::setGUIViewAndCamera(osgViewer::View* view, osg::Camera* cam)
{
_viewerView = view;
_camera = cam;
}
//------------------------------------------------------------------------------
sc::WindowPtr GUIMgr::createWindow(const std::string& name)
{
sc::WindowPtr window = _desktop->createChild<sc::Window>(name);
if( name.empty() )
window->set("id", std::to_string(window->getProps()->getIndex()));
return window;
}
//------------------------------------------------------------------------------
void GUIMgr::init()
{
assert(_viewerView);
if( _desktop && _event_handler )
{
SG_LOG(SG_GUI, SG_WARN, "GUIMgr::init() already initialized.");
return;
}
auto camera = _camera;
if (!camera) {
camera = _viewerView->getCamera();
}
DesktopPtr desktop( new DesktopGroup(camera) );
desktop->handleResize
(
0,
0,
fgGetInt("/sim/startup/xsize"),
fgGetInt("/sim/startup/ysize")
);
_desktop = desktop;
_event_handler = new GUIEventHandler(desktop);
_viewerView->getEventHandlers()
// GUI is on top of everything so lets install as first event handler
.push_front( _event_handler );
sc::Canvas::addPlacementFactory
(
"window",
std::bind(&GUIMgr::addWindowPlacement, this, std::placeholders::_1, std::placeholders::_2)
);
_desktop->getProps()->fireCreatedRecursive();
}
//------------------------------------------------------------------------------
void GUIMgr::shutdown()
{
if( !_desktop && !_event_handler )
{
SG_LOG(SG_GUI, SG_WARN, "GUIMgr::shutdown() not running.");
return;
}
sc::Canvas::removePlacementFactory("window");
if( _desktop )
{
_desktop->destroy();
_desktop.reset();
}
if( _event_handler )
{
_viewerView->removeEventHandler( _event_handler );
_event_handler = 0;
}
_viewerView = nullptr;
}
//------------------------------------------------------------------------------
void GUIMgr::update(double dt)
{
_desktop->update(dt);
}
//------------------------------------------------------------------------------
sc::GroupPtr GUIMgr::getDesktop()
{
return _desktop;
}
//------------------------------------------------------------------------------
void GUIMgr::setInputFocus(const simgear::canvas::WindowPtr& window)
{
static_cast<DesktopGroup*>(_desktop.get())->setFocusWindow(window);
}
//------------------------------------------------------------------------------
bool GUIMgr::grabPointer(const sc::WindowPtr& window)
{
return static_cast<DesktopGroup*>(_desktop.get())->grabPointer(window);
}
//------------------------------------------------------------------------------
void GUIMgr::ungrabPointer(const sc::WindowPtr& window)
{
static_cast<DesktopGroup*>(_desktop.get())->ungrabPointer(window);
}
//------------------------------------------------------------------------------
sc::Placements
GUIMgr::addWindowPlacement( SGPropertyNode* placement,
sc::CanvasPtr canvas )
{
const std::string& id = placement->getStringValue("id");
sc::Placements placements;
sc::WindowPtr window = _desktop->getChild<sc::Window>(id);
if( window )
{
window->setCanvasContent(canvas);
placements.push_back(
sc::PlacementPtr(
new WindowPlacement(placement, window, canvas)
));
}
return placements;
}
// Register the subsystem.
SGSubsystemMgr::Registrant<GUIMgr> registrantGUIMgr(
SGSubsystemMgr::DISPLAY,
{
{"viewer", SGSubsystemMgr::Dependency::HARD},
});

91
src/Canvas/gui_mgr.hxx Normal file
View File

@@ -0,0 +1,91 @@
// Canvas gui/dialog manager
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef CANVAS_GUI_MGR_HXX_
#define CANVAS_GUI_MGR_HXX_
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/canvas/elements/CanvasGroup.hxx>
#include <simgear/props/PropertyBasedMgr.hxx>
#include <simgear/props/propertyObject.hxx>
#include <osg/ref_ptr>
#include <osg/Geode>
#include <osg/MatrixTransform>
namespace osgViewer { class View; }
namespace osg { class Camera; }
namespace osgGA
{
class GUIEventAdapter;
}
class GUIEventHandler;
class GUIMgr : public SGSubsystem
{
public:
GUIMgr();
// Subsystem API.
void init() override;
void shutdown() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "CanvasGUI"; }
simgear::canvas::WindowPtr createWindow(const std::string& name = "");
/**
* Get simgear::canvas::Group containing all windows
*/
simgear::canvas::GroupPtr getDesktop();
/**
* Set the input (keyboard) focus to the given window.
*/
void setInputFocus(const simgear::canvas::WindowPtr& window);
/**
* Grabs the pointer so that all events are passed to this @a window until
* the pointer is ungrabbed with ungrabPointer().
*/
bool grabPointer(const simgear::canvas::WindowPtr& window);
/**
* Releases the grab acquired for this @a window with grabPointer().
*/
void ungrabPointer(const simgear::canvas::WindowPtr& window);
/**
* specify the osgViewer::View and Camera
*/
void setGUIViewAndCamera(osgViewer::View* view, osg::Camera* cam);
protected:
simgear::canvas::GroupPtr _desktop;
osg::ref_ptr<GUIEventHandler> _event_handler;
osg::ref_ptr<osgViewer::View> _viewerView;
osg::ref_ptr<osg::Camera> _camera;
simgear::canvas::Placements
addWindowPlacement( SGPropertyNode* placement,
simgear::canvas::CanvasPtr canvas );
};
#endif /* CANVAS_GUI_MGR_HXX_ */