688 lines
22 KiB
C++
688 lines
22 KiB
C++
// A text on the Canvas
|
|
//
|
|
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Library General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library 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
|
|
// Library General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Library General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
#include "CanvasText.hxx"
|
|
#include <simgear/canvas/Canvas.hxx>
|
|
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
|
#include <simgear/scene/util/parse_color.hxx>
|
|
#include <osg/Version>
|
|
#include <osgDB/Registry>
|
|
#include <osgText/Text>
|
|
|
|
namespace simgear
|
|
{
|
|
namespace canvas
|
|
{
|
|
class Text::TextOSG:
|
|
public osgText::Text
|
|
{
|
|
public:
|
|
|
|
TextOSG(canvas::Text* text);
|
|
|
|
void setFontResolution(int res);
|
|
void setCharacterAspect(float aspect);
|
|
void setLineHeight(float factor);
|
|
void setFill(const std::string& fill);
|
|
void setStroke(const std::string& color);
|
|
void setBackgroundColor(const std::string& fill);
|
|
|
|
SGVec2i sizeForWidth(int w) const;
|
|
osg::Vec2 handleHit(const osg::Vec2f& pos);
|
|
|
|
virtual osg::BoundingBox computeBound() const;
|
|
|
|
protected:
|
|
|
|
canvas::Text *_text_element;
|
|
|
|
virtual void computePositions(unsigned int contextID) const;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
Text::TextOSG::TextOSG(canvas::Text* text):
|
|
_text_element(text)
|
|
{
|
|
setBackdropImplementation(NO_DEPTH_BUFFER);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::TextOSG::setFontResolution(int res)
|
|
{
|
|
TextBase::setFontResolution(res, res);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::TextOSG::setCharacterAspect(float aspect)
|
|
{
|
|
setCharacterSize(getCharacterHeight(), aspect);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::TextOSG::setLineHeight(float factor)
|
|
{
|
|
setLineSpacing(factor - 1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::TextOSG::setFill(const std::string& fill)
|
|
{
|
|
// if( fill == "none" )
|
|
// TODO No text
|
|
// else
|
|
osg::Vec4 color;
|
|
if( parseColor(fill, color) )
|
|
setColor( color );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::TextOSG::setStroke(const std::string& stroke)
|
|
{
|
|
osg::Vec4 color;
|
|
if( stroke == "none" || !parseColor(stroke, color) )
|
|
setBackdropType(NONE);
|
|
else
|
|
{
|
|
setBackdropType(OUTLINE);
|
|
setBackdropColor(color);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::TextOSG::setBackgroundColor(const std::string& fill)
|
|
{
|
|
osg::Vec4 color;
|
|
if( parseColor(fill, color) )
|
|
setBoundingBoxColor( color );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// simplified version of osgText::Text::computeGlyphRepresentation() to
|
|
// just calculate the size for a given weight. Glpyh calculations/creating
|
|
// is not necessary for this...
|
|
SGVec2i Text::TextOSG::sizeForWidth(int w) const
|
|
{
|
|
if( _text.empty() )
|
|
return SGVec2i(0, 0);
|
|
|
|
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
|
|
if( !activefont )
|
|
return SGVec2i(-1, -1);
|
|
|
|
float max_width_safe = _maximumWidth;
|
|
const_cast<TextOSG*>(this)->_maximumWidth = w;
|
|
|
|
SGRecti bb;
|
|
|
|
osg::Vec2 startOfLine_coords(0.0f,0.0f);
|
|
osg::Vec2 cursor(startOfLine_coords);
|
|
osg::Vec2 local(0.0f,0.0f);
|
|
|
|
unsigned int previous_charcode = 0;
|
|
unsigned int line_length = 0;
|
|
bool horizontal = _layout != VERTICAL;
|
|
bool kerning = true;
|
|
|
|
float hr = _characterHeight;
|
|
float wr = hr / getCharacterAspectRatio();
|
|
|
|
// osg should really care more about const :-/
|
|
osgText::String& text = const_cast<osgText::String&>(_text);
|
|
typedef osgText::String::iterator TextIterator;
|
|
|
|
for( TextIterator itr = text.begin(); itr != text.end(); )
|
|
{
|
|
// record the start of the current line
|
|
TextIterator startOfLine_itr = itr;
|
|
|
|
// find the end of the current line.
|
|
osg::Vec2 endOfLine_coords(cursor);
|
|
TextIterator endOfLine_itr =
|
|
const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
|
|
endOfLine_coords, itr, text.end()
|
|
);
|
|
|
|
line_length = endOfLine_itr - startOfLine_itr;
|
|
|
|
// Set line position to correct alignment.
|
|
switch( _layout )
|
|
{
|
|
case LEFT_TO_RIGHT:
|
|
{
|
|
switch( _alignment )
|
|
{
|
|
// nothing to be done for these
|
|
//case LEFT_TOP:
|
|
//case LEFT_CENTER:
|
|
//case LEFT_BOTTOM:
|
|
//case LEFT_BASE_LINE:
|
|
//case LEFT_BOTTOM_BASE_LINE:
|
|
// break;
|
|
case CENTER_TOP:
|
|
case CENTER_CENTER:
|
|
case CENTER_BOTTOM:
|
|
case CENTER_BASE_LINE:
|
|
case CENTER_BOTTOM_BASE_LINE:
|
|
cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
|
break;
|
|
case RIGHT_TOP:
|
|
case RIGHT_CENTER:
|
|
case RIGHT_BOTTOM:
|
|
case RIGHT_BASE_LINE:
|
|
case RIGHT_BOTTOM_BASE_LINE:
|
|
cursor.x() = cursor.x() - endOfLine_coords.x();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case RIGHT_TO_LEFT:
|
|
{
|
|
switch( _alignment )
|
|
{
|
|
case LEFT_TOP:
|
|
case LEFT_CENTER:
|
|
case LEFT_BOTTOM:
|
|
case LEFT_BASE_LINE:
|
|
case LEFT_BOTTOM_BASE_LINE:
|
|
cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
|
|
break;
|
|
case CENTER_TOP:
|
|
case CENTER_CENTER:
|
|
case CENTER_BOTTOM:
|
|
case CENTER_BASE_LINE:
|
|
case CENTER_BOTTOM_BASE_LINE:
|
|
cursor.x() = cursor.x()
|
|
+ (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
|
break;
|
|
// nothing to be done for these
|
|
//case RIGHT_TOP:
|
|
//case RIGHT_CENTER:
|
|
//case RIGHT_BOTTOM:
|
|
//case RIGHT_BASE_LINE:
|
|
//case RIGHT_BOTTOM_BASE_LINE:
|
|
// break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case VERTICAL:
|
|
{
|
|
switch( _alignment )
|
|
{
|
|
// TODO: current behaviour top baselines lined up in both cases - need to implement
|
|
// top of characters alignment - Question is this necessary?
|
|
// ... otherwise, nothing to be done for these 6 cases
|
|
//case LEFT_TOP:
|
|
//case CENTER_TOP:
|
|
//case RIGHT_TOP:
|
|
// break;
|
|
//case LEFT_BASE_LINE:
|
|
//case CENTER_BASE_LINE:
|
|
//case RIGHT_BASE_LINE:
|
|
// break;
|
|
case LEFT_CENTER:
|
|
case CENTER_CENTER:
|
|
case RIGHT_CENTER:
|
|
cursor.y() = cursor.y()
|
|
+ (cursor.y() - endOfLine_coords.y()) * 0.5f;
|
|
break;
|
|
case LEFT_BOTTOM_BASE_LINE:
|
|
case CENTER_BOTTOM_BASE_LINE:
|
|
case RIGHT_BOTTOM_BASE_LINE:
|
|
cursor.y() = cursor.y() - (line_length * _characterHeight);
|
|
break;
|
|
case LEFT_BOTTOM:
|
|
case CENTER_BOTTOM:
|
|
case RIGHT_BOTTOM:
|
|
cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( itr != endOfLine_itr )
|
|
{
|
|
|
|
for(;itr != endOfLine_itr;++itr)
|
|
{
|
|
unsigned int charcode = *itr;
|
|
|
|
osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
|
|
if( glyph )
|
|
{
|
|
float width = (float) (glyph->getWidth()) * wr;
|
|
float height = (float) (glyph->getHeight()) * hr;
|
|
|
|
if( _layout == RIGHT_TO_LEFT )
|
|
{
|
|
cursor.x() -= glyph->getHorizontalAdvance() * wr;
|
|
}
|
|
|
|
// adjust cursor position w.r.t any kerning.
|
|
if( kerning && previous_charcode )
|
|
{
|
|
switch( _layout )
|
|
{
|
|
case LEFT_TO_RIGHT:
|
|
{
|
|
osg::Vec2 delta( activefont->getKerning( previous_charcode,
|
|
charcode,
|
|
_kerningType ) );
|
|
cursor.x() += delta.x() * wr;
|
|
cursor.y() += delta.y() * hr;
|
|
break;
|
|
}
|
|
case RIGHT_TO_LEFT:
|
|
{
|
|
osg::Vec2 delta( activefont->getKerning( charcode,
|
|
previous_charcode,
|
|
_kerningType ) );
|
|
cursor.x() -= delta.x() * wr;
|
|
cursor.y() -= delta.y() * hr;
|
|
break;
|
|
}
|
|
case VERTICAL:
|
|
break; // no kerning when vertical.
|
|
}
|
|
}
|
|
|
|
local = cursor;
|
|
osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
|
|
: glyph->getVerticalBearing() );
|
|
local.x() += bearing.x() * wr;
|
|
local.y() += bearing.y() * hr;
|
|
|
|
// set up the coords of the quad
|
|
osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
|
|
osg::Vec2 lowLeft = local;
|
|
osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
|
|
osg::Vec2 upRight = local + osg::Vec2(width, height);
|
|
|
|
// move the cursor onto the next character.
|
|
// also expand bounding box
|
|
switch( _layout )
|
|
{
|
|
case LEFT_TO_RIGHT:
|
|
cursor.x() += glyph->getHorizontalAdvance() * wr;
|
|
bb.expandBy(lowLeft.x(), lowLeft.y());
|
|
bb.expandBy(upRight.x(), upRight.y());
|
|
break;
|
|
case VERTICAL:
|
|
cursor.y() -= glyph->getVerticalAdvance() * hr;
|
|
bb.expandBy(upLeft.x(), upLeft.y());
|
|
bb.expandBy(lowRight.x(), lowRight.y());
|
|
break;
|
|
case RIGHT_TO_LEFT:
|
|
bb.expandBy(lowRight.x(), lowRight.y());
|
|
bb.expandBy(upLeft.x(), upLeft.y());
|
|
break;
|
|
}
|
|
previous_charcode = charcode;
|
|
}
|
|
}
|
|
|
|
// skip over spaces and return.
|
|
while( itr != text.end() && *itr == ' ' )
|
|
++itr;
|
|
if( itr != text.end() && *itr == '\n' )
|
|
++itr;
|
|
}
|
|
else
|
|
{
|
|
++itr;
|
|
}
|
|
|
|
// move to new line.
|
|
switch( _layout )
|
|
{
|
|
case LEFT_TO_RIGHT:
|
|
{
|
|
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
|
cursor = startOfLine_coords;
|
|
previous_charcode = 0;
|
|
break;
|
|
}
|
|
case RIGHT_TO_LEFT:
|
|
{
|
|
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
|
cursor = startOfLine_coords;
|
|
previous_charcode = 0;
|
|
break;
|
|
}
|
|
case VERTICAL:
|
|
{
|
|
startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
|
|
/ getCharacterAspectRatio();
|
|
cursor = startOfLine_coords;
|
|
previous_charcode = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
|
|
|
|
return bb.size();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
|
|
{
|
|
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, (pos.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();
|
|
|
|
float 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( pos.x() <= threshold )
|
|
{
|
|
osg::Vec2 hit(0, y);
|
|
if( i == 0 || line_numbers[i - 1] != line )
|
|
// first character of line
|
|
hit.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)
|
|
hit.x() = coords[i * 4].x();
|
|
else
|
|
// position at center between characters
|
|
hit.x() = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
|
|
|
|
return hit;
|
|
}
|
|
}
|
|
|
|
// Nothing found -> return position after last character
|
|
return osg::Vec2
|
|
(
|
|
coords.back().x(),
|
|
(_lineCount - 0.5) * line_height
|
|
);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
osg::BoundingBox Text::TextOSG::computeBound() const
|
|
{
|
|
osg::BoundingBox bb = osgText::Text::computeBound();
|
|
|
|
#if OSG_VERSION_LESS_THAN(3,1,0)
|
|
if( bb.valid() )
|
|
{
|
|
// TODO bounding box still doesn't seem always right (eg. with center
|
|
// horizontal alignment not completely accurate)
|
|
bb._min.y() += _offset.y();
|
|
bb._max.y() += _offset.y();
|
|
}
|
|
#endif
|
|
|
|
return bb;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::TextOSG::computePositions(unsigned int contextID) const
|
|
{
|
|
if( _textureGlyphQuadMap.empty() || _layout == VERTICAL )
|
|
return osgText::Text::computePositions(contextID);
|
|
|
|
// TODO check when it can be larger
|
|
assert( _textureGlyphQuadMap.size() == 1 );
|
|
|
|
const GlyphQuads& quads = _textureGlyphQuadMap.begin()->second;
|
|
const GlyphQuads::Glyphs& glyphs = quads._glyphs;
|
|
const GlyphQuads::Coords2& coords = quads._coords;
|
|
const GlyphQuads::LineNumbers& line_numbers = quads._lineNumbers;
|
|
|
|
float wr = _characterHeight / getCharacterAspectRatio();
|
|
|
|
size_t cur_line = static_cast<size_t>(-1);
|
|
for(size_t i = 0; i < glyphs.size(); ++i)
|
|
{
|
|
// Check horizontal offsets
|
|
|
|
bool first_char = cur_line != line_numbers[i];
|
|
cur_line = line_numbers[i];
|
|
|
|
bool last_char = (i + 1 == glyphs.size())
|
|
|| (cur_line != line_numbers[i + 1]);
|
|
|
|
if( first_char || last_char )
|
|
{
|
|
// From osg/src/osgText/Text.cpp:
|
|
//
|
|
// osg::Vec2 upLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
|
|
// osg::Vec2 lowLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
|
|
// osg::Vec2 lowRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
|
|
// osg::Vec2 upRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
|
|
|
|
float left = coords[i * 4].x(),
|
|
right = coords[i * 4 + 2].x(),
|
|
width = glyphs[i]->getWidth() * wr;
|
|
|
|
// (local + width + fHoriz) - (local - fHoriz) = width + 2*fHoriz | -width
|
|
float margin = 0.5f * (right - left - width),
|
|
cursor_x = left + margin
|
|
- glyphs[i]->getHorizontalBearing().x() * wr;
|
|
|
|
if( first_char )
|
|
{
|
|
if( cur_line == 0 || cursor_x < _textBB._min.x() )
|
|
_textBB._min.x() = cursor_x;
|
|
}
|
|
|
|
if( last_char )
|
|
{
|
|
float cursor_w = cursor_x + glyphs[i]->getHorizontalAdvance() * wr;
|
|
|
|
if( cur_line == 0 || cursor_w > _textBB._max.x() )
|
|
_textBB._max.x() = cursor_w;
|
|
}
|
|
}
|
|
}
|
|
|
|
return osgText::Text::computePositions(contextID);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const std::string Text::TYPE_NAME = "text";
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::staticInit()
|
|
{
|
|
if( isInit<Text>() )
|
|
return;
|
|
|
|
osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
|
|
|
|
addStyle("fill", "color", &TextOSG::setFill, text);
|
|
addStyle("background", "color", &TextOSG::setBackgroundColor, text);
|
|
addStyle("stroke", "color", &TextOSG::setStroke, text);
|
|
addStyle("character-size",
|
|
"numeric",
|
|
static_cast<
|
|
void (TextOSG::*)(float)
|
|
> (&TextOSG::setCharacterSize),
|
|
text);
|
|
addStyle("character-aspect-ratio",
|
|
"numeric",
|
|
&TextOSG::setCharacterAspect, text);
|
|
addStyle("line-height", "numeric", &TextOSG::setLineHeight, text);
|
|
addStyle("font-resolution", "numeric", &TextOSG::setFontResolution, text);
|
|
addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text);
|
|
// TEXT = 1 default
|
|
// BOUNDINGBOX = 2
|
|
// FILLEDBOUNDINGBOX = 4
|
|
// ALIGNMENT = 8
|
|
addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text);
|
|
addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text);
|
|
addStyle("font", "", &Text::setFont);
|
|
addStyle("alignment", "", &Text::setAlignment);
|
|
addStyle("text", "", &Text::setText, false);
|
|
|
|
osgDB::Registry* reg = osgDB::Registry::instance();
|
|
if( !reg->getReaderWriterForExtension("ttf") )
|
|
SG_LOG(SG_GL, SG_ALERT, "canvas::Text: Missing 'ttf' font reader");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Text::Text( const CanvasWeakPtr& canvas,
|
|
const SGPropertyNode_ptr& node,
|
|
const Style& parent_style,
|
|
Element* parent ):
|
|
Element(canvas, node, parent_style, parent),
|
|
_text( new Text::TextOSG(this) )
|
|
{
|
|
staticInit();
|
|
|
|
setDrawable(_text);
|
|
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
|
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
|
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
|
|
|
setupStyle();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Text::~Text()
|
|
{
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::setText(const char* text)
|
|
{
|
|
_text->setText(text, osgText::String::ENCODING_UTF8);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::setFont(const char* name)
|
|
{
|
|
_text->setFont( Canvas::getSystemAdapter()->getFont(name) );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void Text::setAlignment(const char* align)
|
|
{
|
|
const std::string align_string(align);
|
|
if( 0 ) return;
|
|
#define ENUM_MAPPING(enum_val, string_val) \
|
|
else if( align_string == string_val )\
|
|
_text->setAlignment( osgText::Text::enum_val );
|
|
#include "text-alignment.hxx"
|
|
#undef ENUM_MAPPING
|
|
else
|
|
{
|
|
if( !align_string.empty() )
|
|
SG_LOG
|
|
(
|
|
SG_GENERAL,
|
|
SG_WARN,
|
|
"canvas::Text: unknown alignment '" << align_string << "'"
|
|
);
|
|
_text->setAlignment(osgText::Text::LEFT_BASE_LINE);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#if 0
|
|
const char* Text::getAlignment() const
|
|
{
|
|
switch( _text->getAlignment() )
|
|
{
|
|
#define ENUM_MAPPING(enum_val, string_val) \
|
|
case osgText::Text::enum_val:\
|
|
return string_val;
|
|
#include "text-alignment.hxx"
|
|
#undef ENUM_MAPPING
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
int Text::heightForWidth(int w) const
|
|
{
|
|
return _text->sizeForWidth(w).y();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int Text::maxWidth() const
|
|
{
|
|
return _text->sizeForWidth(INT_MAX).x();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
|
|
{
|
|
return _text->handleHit(pos);
|
|
}
|
|
|
|
} // namespace canvas
|
|
} // namespace simgear
|