1
0
Fork 0

Canvas: Allow using canvases as PUI widgets.

- Add new widget type canvas
 - Set canvas view dimension from the gui xml.
 - Expose mouse events to canvas widget properties.
This commit is contained in:
Thomas Geymayer 2012-07-04 13:15:12 +02:00
parent 36fe51c7f0
commit 373d511c69
12 changed files with 312 additions and 15 deletions

View file

@ -18,11 +18,15 @@
#include "canvas.hxx"
#include "elements/group.hxx"
#include <Canvas/property_helper.hxx>
#include <Main/globals.hxx>
#include <Viewer/renderer.hxx>
#include <osg/Camera>
#include <osg/Geode>
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <iostream>
@ -97,13 +101,13 @@ Canvas::Canvas():
_status(0),
_sampling_dirty(false),
_color_dirty(true),
_node(0)
_node(0),
_render_always(false)
{
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
CameraCullCallback *camera_callback = new CameraCullCallback;
_camera_callback = camera_callback;
_cull_callback = new PlacementCullCallback(this, camera_callback);
_camera_callback = new CameraCullCallback;
_cull_callback = new PlacementCullCallback(this, _camera_callback);
}
//------------------------------------------------------------------------------
@ -254,6 +258,9 @@ void Canvas::update(double delta_time_sec)
_cull_callback
);
}
if( _render_always )
_camera_callback->enableRendering();
}
//------------------------------------------------------------------------------
@ -398,11 +405,38 @@ void Canvas::valueChanged(SGPropertyNode* node)
|| node->getNameString() == "coverage-samples"
|| node->getNameString() == "color-samples" )
_sampling_dirty = true;
else if( node->getNameString() == "render-always" )
_render_always = node->getBoolValue();
}
_root_group->valueChanged(node);
}
//------------------------------------------------------------------------------
GLuint Canvas::getTexId() const
{
osg::Texture2D* tex = _texture.getTexture();
if( !tex )
return 0;
osgViewer::Viewer::Contexts contexts;
globals->get_renderer()->getViewer()->getContexts(contexts);
if( contexts.empty() )
return 0;
osg::State* state = contexts[0]->getState();
if( !state )
return 0;
osg::Texture::TextureObject* tobj =
tex->getTextureObject( state->getContextID() );
if( !tobj )
return 0;
return tobj->_id;
}
//------------------------------------------------------------------------------
void Canvas::setStatusFlags(unsigned int flags, bool set)
{

View file

@ -31,6 +31,7 @@ namespace canvas
class Group;
}
class CameraCullCallback;
class Canvas:
public SGPropertyChangeListener
{
@ -71,6 +72,8 @@ class Canvas:
SGPropertyNode * child );
virtual void valueChanged (SGPropertyNode * node);
GLuint getTexId() const;
private:
Canvas(const Canvas&); // = delete;
@ -93,8 +96,11 @@ class Canvas:
SGPropertyNode_ptr _node;
std::vector<SGPropertyNode_ptr> _color_background;
osg::ref_ptr<osg::NodeCallback> _camera_callback;
osg::ref_ptr<CameraCullCallback> _camera_callback;
osg::ref_ptr<osg::NodeCallback> _cull_callback;
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
std::vector<SGPropertyNode*> _dirty_placements;
std::vector<Placements> _placements;

View file

@ -107,6 +107,16 @@ void CanvasMgr::childRemoved( SGPropertyNode * parent,
}
}
//------------------------------------------------------------------------------
unsigned int CanvasMgr::getCanvasTexId(size_t index) const
{
if( index >= _canvases.size()
|| !_canvases[index] )
return 0;
return _canvases[index]->getTexId();
}
//------------------------------------------------------------------------------
void CanvasMgr::textureAdded(SGPropertyNode* node)
{
@ -119,10 +129,8 @@ void CanvasMgr::textureAdded(SGPropertyNode* node)
_canvases.resize(index + 1);
}
else
{
else if( _canvases[index] )
SG_LOG(SG_GL, SG_WARN, "texture[" << index << "] already exists!");
}
_canvases[index].reset( new Canvas() );
_canvases[index]->reset(node);

View file

@ -50,6 +50,14 @@ class CanvasMgr:
virtual void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child );
/**
* Get OpenGL texture name for given canvas
*
* @param Index of canvas
* @return OpenGL texture name
*/
unsigned int getCanvasTexId(size_t index) const;
private:
/** Root node for everything concerning the canvas system */

View file

@ -2,6 +2,7 @@ include(FlightGearComponent)
set(SOURCES
AirportList.cxx
CanvasWidget.cxx
MapWidget.cxx
WaypointList.cxx
dialog.cxx
@ -21,6 +22,7 @@ set(SOURCES
set(HEADERS
AirportList.hxx
CanvasWidget.hxx
MapWidget.hxx
WaypointList.hxx
dialog.hxx

168
src/GUI/CanvasWidget.cxx Normal file
View file

@ -0,0 +1,168 @@
/*
* CanvasWidget.cxx
*
* Created on: 03.07.2012
* Author: tom
*/
#include "CanvasWidget.hxx"
#include <Canvas/canvas_mgr.hxx>
#include <Main/fg_os.hxx> // fgGetKeyModifiers()
#include <Scripting/NasalSys.hxx>
//------------------------------------------------------------------------------
CanvasWidget::CanvasWidget( int x, int y,
int width, int height,
SGPropertyNode* props,
const std::string& module ):
puObject(x, y, width, height),
_canvas_mgr( dynamic_cast<CanvasMgr*>(globals->get_subsystem("Canvas")) ),
_tex_id(0),
_no_tex_cnt(0)
{
if( !_canvas_mgr )
{
SG_LOG(SG_GENERAL, SG_ALERT, "CanvasWidget: failed to get canvas manager!");
return;
}
// Get the first unused canvas slot
SGPropertyNode* canvas_root = fgGetNode("/canvas", true);
for(int index = 0;; ++index)
{
if( !canvas_root->getChild("texture", index) )
{
int view[2] = {
// Get canvas viewport size. If not specified use the widget dimensions
props->getIntValue("view[0]", width),
props->getIntValue("view[1]", height)
};
_canvas = canvas_root->getChild("texture", index, true);
_canvas->setIntValue("size[0]", view[0] * 2); // use higher resolution
_canvas->setIntValue("size[1]", view[1] * 2); // for antialias
_canvas->setIntValue("view[0]", view[0]);
_canvas->setIntValue("view[1]", view[1]);
_canvas->setBoolValue("render-always", true);
_canvas->setStringValue( "name",
props->getStringValue("name", "gui-anonymous") );
SGPropertyNode* input = _canvas->getChild("input", 0, true);
_mouse_x = input->getChild("mouse-x", 0, true);
_mouse_y = input->getChild("mouse-y", 0, true);
_mouse_down = input->getChild("mouse-down", 0, true);
_mouse_drag = input->getChild("mouse-drag", 0, true);
SGPropertyNode *nasal = props->getNode("nasal");
if( !nasal )
break;
FGNasalSys *nas =
dynamic_cast<FGNasalSys*>(globals->get_subsystem("nasal"));
if( !nas )
SG_LOG( SG_GENERAL,
SG_ALERT,
"CanvasWidget: Failed to get nasal subsystem!" );
const std::string file = std::string("__canvas:")
+ _canvas->getStringValue("name");
SGPropertyNode *load = nasal->getNode("load");
if( load )
{
const char *s = load->getStringValue();
nas->handleCommand(module.c_str(), file.c_str(), s, _canvas);
}
break;
}
}
}
//------------------------------------------------------------------------------
CanvasWidget::~CanvasWidget()
{
if( _canvas )
_canvas->getParent()
->removeChild(_canvas->getName(), _canvas->getIndex(), false);
}
//------------------------------------------------------------------------------
void CanvasWidget::doHit(int button, int updown, int x, int y)
{
puObject::doHit(button, updown, x, y);
// CTRL allows resizing and SHIFT allows moving the window
if( fgGetKeyModifiers() & (KEYMOD_CTRL | KEYMOD_SHIFT) )
return;
_mouse_x->setIntValue(x - abox.min[0]);
_mouse_y->setIntValue(y - abox.min[1]);
if( updown == PU_DRAG )
_mouse_drag->setIntValue(button);
else if( updown == PU_DOWN )
_mouse_down->setIntValue(button);
if( button != active_mouse_button )
return;
if (updown == PU_UP)
puDeactivateWidget();
else if (updown == PU_DOWN)
puSetActiveWidget(this, x, y);
}
//------------------------------------------------------------------------------
int CanvasWidget::checkKey(int key, int updown)
{
return puObject::checkKey(key, updown);
}
//------------------------------------------------------------------------------
void CanvasWidget::setSize(int w, int h)
{
puObject::setSize(w, h);
_canvas->setIntValue("view[0]", w);
_canvas->setIntValue("view[1]", h);
}
//------------------------------------------------------------------------------
void CanvasWidget::draw(int dx, int dy)
{
if( !_tex_id )
{
_tex_id = _canvas_mgr->getCanvasTexId(_canvas->getIndex());
// Normally we should be able to get the texture after one frame. I don't
// know if there are circumstances where it can take longer, so we don't
// log a warning message until we have tried a few times.
if( !_tex_id )
{
if( ++_no_tex_cnt == 5 )
SG_LOG(SG_GENERAL, SG_WARN, "CanvasWidget: failed to get texture!");
return;
}
else
{
if( _no_tex_cnt >= 5 )
SG_LOG
(
SG_GENERAL,
SG_INFO,
"CanvasWidget: got texture after " << _no_tex_cnt << " tries."
);
_no_tex_cnt = 0;
}
}
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _tex_id);
glBegin( GL_QUADS );
glColor3f(1,1,1);
glTexCoord2f(0,0); glVertex2f(dx + abox.min[0], dy + abox.min[1]);
glTexCoord2f(1,0); glVertex2f(dx + abox.max[0], dy + abox.min[1]);
glTexCoord2f(1,1); glVertex2f(dx + abox.max[0], dy + abox.max[1]);
glTexCoord2f(0,1); glVertex2f(dx + abox.min[0], dy + abox.max[1]);
glEnd();
glDisable(GL_TEXTURE_2D);
}

47
src/GUI/CanvasWidget.hxx Normal file
View file

@ -0,0 +1,47 @@
/*
* CanvasWidget.hxx
*
* Created on: 03.07.2012
* Author: tom
*/
#ifndef CANVASWIDGET_HXX_
#define CANVASWIDGET_HXX_
#include <Main/fg_props.hxx>
#include <plib/pu.h>
class CanvasMgr;
class CanvasWidget:
public puObject
{
public:
CanvasWidget( int x, int y,
int width, int height,
SGPropertyNode* props,
const std::string& module );
virtual ~CanvasWidget();
virtual void doHit (int button, int updown, int x, int y);
virtual int checkKey(int key , int updown);
virtual void setSize ( int w, int h );
virtual void draw(int dx, int dy);
private:
CanvasMgr *_canvas_mgr; // TODO maybe we should store this in some central
// location or make it static...
GLuint _tex_id; //<! OpenGL texture id if canvas
size_t _no_tex_cnt;//<! Count since how many frames we were not
// able to get the texture (for debugging)
SGPropertyNode_ptr _canvas; //<! Canvas root property node
SGPropertyNode *_mouse_x,
*_mouse_y,
*_mouse_down,
*_mouse_drag;
};
#endif /* CANVASWIDGET_HXX_ */

View file

@ -18,6 +18,7 @@
#include "property_list.hxx"
#include "layout.hxx"
#include "WaypointList.hxx"
#include "CanvasWidget.hxx"
#include "MapWidget.hxx"
#include "FGFontCache.hxx"
#include "FGColor.hxx"
@ -827,6 +828,13 @@ FGPUIDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeigh
MapWidget* mapWidget = new MapWidget(x, y, x + width, y + height);
setupObject(mapWidget, props);
return mapWidget;
} else if (type == "canvas") {
CanvasWidget* canvasWidget = new CanvasWidget( x, y,
x + width, y + height,
props,
_module );
setupObject(canvasWidget, props);
return canvasWidget;
} else if (type == "combo") {
fgComboBox *obj = new fgComboBox(x, y, x + width, y + height, props,
props->getBoolValue("editable", false));

View file

@ -132,9 +132,9 @@ public:
/**
* Get the OSG camera for drawing this gauge.
*/
osg::Camera* getCamera() { return camera.get(); }
osg::Camera* getCamera() const { return camera.get(); }
osg::Texture2D* getTexture() { return texture.get(); }
osg::Texture2D* getTexture() const { return texture.get(); }
//void setTexture(osg::Texture2D* t) { texture = t; }
// Real initialization function. Bad name.

View file

@ -1163,7 +1163,7 @@ bool fgInitSubsystems() {
////////////////////////////////////////////////////////////////////
// Initialize the canvas 2d drawing subsystem.
////////////////////////////////////////////////////////////////////
globals->add_subsystem("Canvas2D", new CanvasMgr, SGSubsystemMgr::DISPLAY);
globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY);
////////////////////////////////////////////////////////////////////
// Initialise the ATIS Manager

View file

@ -816,11 +816,12 @@ naRef FGNasalSys::parse(const char* filename, const char* buf, int len)
return naBindFunction(_context, code, _globals);
}
bool FGNasalSys::handleCommand(const SGPropertyNode* arg)
bool FGNasalSys::handleCommand( const char* moduleName,
const char* fileName,
const char* src,
const SGPropertyNode* arg )
{
const char* nasal = arg->getStringValue("script");
const char* moduleName = arg->getStringValue("module");
naRef code = parse(arg->getPath(true).c_str(), nasal, strlen(nasal));
naRef code = parse(fileName, src, strlen(src));
if(naIsNil(code)) return false;
// Commands can be run "in" a module. Make sure that module
@ -845,6 +846,17 @@ bool FGNasalSys::handleCommand(const SGPropertyNode* arg)
return true;
}
bool FGNasalSys::handleCommand(const SGPropertyNode* arg)
{
const char* src = arg->getStringValue("script");
const char* moduleName = arg->getStringValue("module");
return handleCommand( moduleName,
arg ? arg->getPath(true).c_str() : moduleName,
src,
arg );
}
// settimer(func, dt, simtime) extension function. The first argument
// is a Nasal function to call, the second is a delta time (from now),
// in seconds. The third, if present, is a boolean value indicating

View file

@ -102,6 +102,10 @@ public:
naRef cmdArgGhost();
// Callbacks for command and timer bindings
virtual bool handleCommand( const char* moduleName,
const char* fileName,
const char* src,
const SGPropertyNode* arg = 0 );
virtual bool handleCommand(const SGPropertyNode* arg);
bool createModule(const char* moduleName, const char* fileName,