Canvas: Support for text selection.
- Expose nearest hit for text/character selection - Fix culling
This commit is contained in:
parent
91c3f63110
commit
5f08e10c0a
3 changed files with 134 additions and 18 deletions
|
@ -21,8 +21,11 @@
|
||||||
|
|
||||||
#include <Canvas/property_helper.hxx>
|
#include <Canvas/property_helper.hxx>
|
||||||
#include <Main/globals.hxx>
|
#include <Main/globals.hxx>
|
||||||
|
#include <Viewer/CameraGroup.hxx>
|
||||||
#include <Viewer/renderer.hxx>
|
#include <Viewer/renderer.hxx>
|
||||||
|
|
||||||
|
#include <simgear/scene/util/RenderConstants.hxx>
|
||||||
|
|
||||||
#include <osg/Camera>
|
#include <osg/Camera>
|
||||||
#include <osg/Geode>
|
#include <osg/Geode>
|
||||||
#include <osgText/Text>
|
#include <osgText/Text>
|
||||||
|
@ -41,7 +44,8 @@ class CameraCullCallback:
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CameraCullCallback():
|
CameraCullCallback():
|
||||||
_render( true )
|
_render( true ),
|
||||||
|
_render_frame( 0 )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,14 +59,17 @@ class CameraCullCallback:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool _render;
|
bool _render;
|
||||||
|
unsigned int _render_frame;
|
||||||
|
|
||||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
{
|
{
|
||||||
if( _render )
|
if( !_render && nv->getTraversalNumber() != _render_frame )
|
||||||
{
|
return;
|
||||||
traverse(node, nv);
|
|
||||||
_render = false;
|
traverse(node, nv);
|
||||||
}
|
|
||||||
|
_render = false;
|
||||||
|
_render_frame = nv->getTraversalNumber();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,7 +94,9 @@ class PlacementCullCallback:
|
||||||
|
|
||||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
{
|
{
|
||||||
_camera_cull->enableRendering();
|
if( nv->getTraversalMask() & simgear::MODEL_BIT )
|
||||||
|
_camera_cull->enableRendering();
|
||||||
|
|
||||||
traverse(node, nv);
|
traverse(node, nv);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -419,13 +428,16 @@ GLuint Canvas::getTexId() const
|
||||||
if( !tex )
|
if( !tex )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
osgViewer::Viewer::Contexts contexts;
|
// osgViewer::Viewer::Contexts contexts;
|
||||||
globals->get_renderer()->getViewer()->getContexts(contexts);
|
// globals->get_renderer()->getViewer()->getContexts(contexts);
|
||||||
|
//
|
||||||
|
// if( contexts.empty() )
|
||||||
|
// return 0;
|
||||||
|
|
||||||
if( contexts.empty() )
|
osg::Camera* guiCamera =
|
||||||
return 0;
|
flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
|
||||||
|
|
||||||
osg::State* state = contexts[0]->getState();
|
osg::State* state = guiCamera->getGraphicsContext()->getState(); //contexts[0]->getState();
|
||||||
if( !state )
|
if( !state )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,96 @@
|
||||||
|
|
||||||
namespace canvas
|
namespace canvas
|
||||||
{
|
{
|
||||||
|
class Text::TextOSG:
|
||||||
|
public osgText::Text
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
osg::Vec2 handleHit(float x, float y);
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
osg::Vec2 Text::TextOSG::handleHit(float x, float y)
|
||||||
|
{
|
||||||
|
float line_height = _characterHeight + _lineSpacing;
|
||||||
|
|
||||||
|
// TODO check with align other than TOP
|
||||||
|
float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
|
||||||
|
size_t line = std::max<int>(0, (y - first_line_y) / line_height);
|
||||||
|
|
||||||
|
if( _textureGlyphQuadMap.empty() )
|
||||||
|
return osg::Vec2(-1, -1);
|
||||||
|
|
||||||
|
// TODO check when it can be larger
|
||||||
|
assert( _textureGlyphQuadMap.size() == 1 );
|
||||||
|
|
||||||
|
const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
|
||||||
|
const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
|
||||||
|
const GlyphQuads::Coords2& coords = glyphquad._coords;
|
||||||
|
const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
|
||||||
|
|
||||||
|
const float HIT_FRACTION = 0.6;
|
||||||
|
const float character_width = getCharacterHeight()
|
||||||
|
* getCharacterAspectRatio();
|
||||||
|
|
||||||
|
y = (line + 0.5) * line_height;
|
||||||
|
|
||||||
|
bool line_found = false;
|
||||||
|
for(size_t i = 0; i < line_numbers.size(); ++i)
|
||||||
|
{
|
||||||
|
if( line_numbers[i] != line )
|
||||||
|
{
|
||||||
|
if( !line_found )
|
||||||
|
{
|
||||||
|
if( line_numbers[i] < line )
|
||||||
|
// Wait for the correct line...
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We have already passed the correct line -> It's empty...
|
||||||
|
return osg::Vec2(0, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next line and not returned -> not before any character
|
||||||
|
// -> return position after last character of line
|
||||||
|
return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
|
||||||
|
}
|
||||||
|
|
||||||
|
line_found = true;
|
||||||
|
|
||||||
|
// Get threshold for mouse x position for setting cursor before or after
|
||||||
|
// current character
|
||||||
|
float threshold = coords[i * 4].x()
|
||||||
|
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
|
||||||
|
* character_width;
|
||||||
|
|
||||||
|
if( x <= threshold )
|
||||||
|
{
|
||||||
|
if( i == 0 || line_numbers[i - 1] != line )
|
||||||
|
// first character of line
|
||||||
|
x = coords[i * 4].x();
|
||||||
|
else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
|
||||||
|
// If previous character width is zero set to begin of next character
|
||||||
|
// (Happens eg. with spaces)
|
||||||
|
x = coords[i * 4].x();
|
||||||
|
else
|
||||||
|
// position at center between characters
|
||||||
|
x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
|
||||||
|
|
||||||
|
return osg::Vec2(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing found -> return position after last character
|
||||||
|
return osg::Vec2
|
||||||
|
(
|
||||||
|
coords.back().x(),
|
||||||
|
(_lineCount - 0.5) * line_height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Text::Text(SGPropertyNode_ptr node):
|
Text::Text(SGPropertyNode_ptr node):
|
||||||
Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
|
Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
|
||||||
_text( new osgText::Text ),
|
_text( new Text::TextOSG() ),
|
||||||
_font_size( 0 ),
|
_font_size( 0 ),
|
||||||
_font_aspect( 0 )
|
_font_aspect( 0 )
|
||||||
{
|
{
|
||||||
|
@ -146,17 +231,25 @@ namespace canvas
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Text::childChanged(SGPropertyNode* child)
|
void Text::childChanged(SGPropertyNode* child)
|
||||||
{
|
{
|
||||||
if( _font_size == child || _font_aspect == child )
|
const std::string& name = child->getNameString();
|
||||||
|
|
||||||
|
if( name == "hit-y" )
|
||||||
|
handleHit
|
||||||
|
(
|
||||||
|
_node->getFloatValue("hit-x"),
|
||||||
|
_node->getFloatValue("hit-y")
|
||||||
|
);
|
||||||
|
else if( _font_size == child || _font_aspect == child )
|
||||||
_attributes_dirty |= FONT_SIZE;
|
_attributes_dirty |= FONT_SIZE;
|
||||||
else if( child->getNameString() == "text" )
|
else if( name == "text" )
|
||||||
_text->setText
|
_text->setText
|
||||||
(
|
(
|
||||||
osgText::String( child->getStringValue(),
|
osgText::String( child->getStringValue(),
|
||||||
osgText::String::ENCODING_UTF8 )
|
osgText::String::ENCODING_UTF8 )
|
||||||
);
|
);
|
||||||
else if( child->getNameString() == "max-width" )
|
else if( name == "max-width" )
|
||||||
_text->setMaximumWidth( child->getFloatValue() );
|
_text->setMaximumWidth( child->getFloatValue() );
|
||||||
else if( child->getNameString() == "font" )
|
else if( name == "font" )
|
||||||
setFont( child->getStringValue() );
|
setFont( child->getStringValue() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +265,14 @@ namespace canvas
|
||||||
_text->setBoundingBoxColor(color);
|
_text->setBoundingBoxColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
void Text::handleHit(float x, float y)
|
||||||
|
{
|
||||||
|
const osg::Vec2& pos = _text->handleHit(x, y);
|
||||||
|
_node->setFloatValue("cursor-x", pos.x());
|
||||||
|
_node->setFloatValue("cursor-y", pos.y());
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
Text::font_ptr Text::getFont(const std::string& name)
|
Text::font_ptr Text::getFont(const std::string& name)
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,7 +49,8 @@ namespace canvas
|
||||||
FONT_SIZE = LAST_ATTRIBUTE << 1, // Font size and aspect ration
|
FONT_SIZE = LAST_ATTRIBUTE << 1, // Font size and aspect ration
|
||||||
};
|
};
|
||||||
|
|
||||||
osg::ref_ptr<osgText::Text> _text;
|
class TextOSG;
|
||||||
|
osg::ref_ptr<TextOSG> _text;
|
||||||
|
|
||||||
SGPropertyNode_ptr _font_size,
|
SGPropertyNode_ptr _font_size,
|
||||||
_font_aspect;
|
_font_aspect;
|
||||||
|
@ -58,6 +59,8 @@ namespace canvas
|
||||||
virtual void colorChanged(const osg::Vec4& color);
|
virtual void colorChanged(const osg::Vec4& color);
|
||||||
virtual void colorFillChanged(const osg::Vec4& color);
|
virtual void colorFillChanged(const osg::Vec4& color);
|
||||||
|
|
||||||
|
void handleHit(float x, float y);
|
||||||
|
|
||||||
typedef osg::ref_ptr<osgText::Font> font_ptr;
|
typedef osg::ref_ptr<osgText::Font> font_ptr;
|
||||||
static font_ptr getFont(const std::string& name);
|
static font_ptr getFont(const std::string& name);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue