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 <Main/globals.hxx>
|
||||
#include <Viewer/CameraGroup.hxx>
|
||||
#include <Viewer/renderer.hxx>
|
||||
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
|
||||
#include <osg/Camera>
|
||||
#include <osg/Geode>
|
||||
#include <osgText/Text>
|
||||
|
@ -41,7 +44,8 @@ class CameraCullCallback:
|
|||
public:
|
||||
|
||||
CameraCullCallback():
|
||||
_render( true )
|
||||
_render( true ),
|
||||
_render_frame( 0 )
|
||||
{}
|
||||
|
||||
/**
|
||||
|
@ -55,14 +59,17 @@ class CameraCullCallback:
|
|||
private:
|
||||
|
||||
bool _render;
|
||||
unsigned int _render_frame;
|
||||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
if( _render )
|
||||
{
|
||||
traverse(node, nv);
|
||||
_render = false;
|
||||
}
|
||||
if( !_render && nv->getTraversalNumber() != _render_frame )
|
||||
return;
|
||||
|
||||
traverse(node, nv);
|
||||
|
||||
_render = false;
|
||||
_render_frame = nv->getTraversalNumber();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -87,7 +94,9 @@ class PlacementCullCallback:
|
|||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
_camera_cull->enableRendering();
|
||||
if( nv->getTraversalMask() & simgear::MODEL_BIT )
|
||||
_camera_cull->enableRendering();
|
||||
|
||||
traverse(node, nv);
|
||||
}
|
||||
};
|
||||
|
@ -419,13 +428,16 @@ GLuint Canvas::getTexId() const
|
|||
if( !tex )
|
||||
return 0;
|
||||
|
||||
osgViewer::Viewer::Contexts contexts;
|
||||
globals->get_renderer()->getViewer()->getContexts(contexts);
|
||||
// osgViewer::Viewer::Contexts contexts;
|
||||
// globals->get_renderer()->getViewer()->getContexts(contexts);
|
||||
//
|
||||
// if( contexts.empty() )
|
||||
// return 0;
|
||||
|
||||
if( contexts.empty() )
|
||||
return 0;
|
||||
osg::Camera* guiCamera =
|
||||
flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
|
||||
|
||||
osg::State* state = contexts[0]->getState();
|
||||
osg::State* state = guiCamera->getGraphicsContext()->getState(); //contexts[0]->getState();
|
||||
if( !state )
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -26,11 +26,96 @@
|
|||
|
||||
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):
|
||||
Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
|
||||
_text( new osgText::Text ),
|
||||
_text( new Text::TextOSG() ),
|
||||
_font_size( 0 ),
|
||||
_font_aspect( 0 )
|
||||
{
|
||||
|
@ -146,17 +231,25 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
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;
|
||||
else if( child->getNameString() == "text" )
|
||||
else if( name == "text" )
|
||||
_text->setText
|
||||
(
|
||||
osgText::String( child->getStringValue(),
|
||||
osgText::String::ENCODING_UTF8 )
|
||||
);
|
||||
else if( child->getNameString() == "max-width" )
|
||||
else if( name == "max-width" )
|
||||
_text->setMaximumWidth( child->getFloatValue() );
|
||||
else if( child->getNameString() == "font" )
|
||||
else if( name == "font" )
|
||||
setFont( child->getStringValue() );
|
||||
}
|
||||
|
||||
|
@ -172,6 +265,14 @@ namespace canvas
|
|||
_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)
|
||||
{
|
||||
|
|
|
@ -49,7 +49,8 @@ namespace canvas
|
|||
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,
|
||||
_font_aspect;
|
||||
|
@ -58,6 +59,8 @@ namespace canvas
|
|||
virtual void colorChanged(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;
|
||||
static font_ptr getFont(const std::string& name);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue