1 // A text on the canvas
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include <Canvas/property_helper.hxx>
22 #include <Main/globals.hxx>
23 #include <Main/fg_props.hxx>
25 #include <osgText/Text>
34 TextOSG(canvas::Text* text);
36 void setCharacterAspect(float aspect);
37 void setFill(const std::string& fill);
38 void setBackgroundColor(const std::string& fill);
40 osg::Vec2 handleHit(float x, float y);
42 virtual osg::BoundingBox computeBound() const;
46 canvas::Text *_text_element;
49 //----------------------------------------------------------------------------
50 Text::TextOSG::TextOSG(canvas::Text* text):
56 //----------------------------------------------------------------------------
57 void Text::TextOSG::setCharacterAspect(float aspect)
59 setCharacterSize(getCharacterHeight(), aspect);
62 //----------------------------------------------------------------------------
63 void Text::TextOSG::setFill(const std::string& fill)
65 // if( fill == "none" )
68 setColor( parseColor(fill) );
71 //----------------------------------------------------------------------------
72 void Text::TextOSG::setBackgroundColor(const std::string& fill)
74 setBoundingBoxColor( parseColor(fill) );
77 //----------------------------------------------------------------------------
78 osg::Vec2 Text::TextOSG::handleHit(float x, float y)
80 float line_height = _characterHeight + _lineSpacing;
82 // TODO check with align other than TOP
83 float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
84 size_t line = std::max<int>(0, (y - first_line_y) / line_height);
86 if( _textureGlyphQuadMap.empty() )
87 return osg::Vec2(-1, -1);
89 // TODO check when it can be larger
90 assert( _textureGlyphQuadMap.size() == 1 );
92 const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
93 const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
94 const GlyphQuads::Coords2& coords = glyphquad._coords;
95 const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
97 const float HIT_FRACTION = 0.6;
98 const float character_width = getCharacterHeight()
99 * getCharacterAspectRatio();
101 y = (line + 0.5) * line_height;
103 bool line_found = false;
104 for(size_t i = 0; i < line_numbers.size(); ++i)
106 if( line_numbers[i] != line )
110 if( line_numbers[i] < line )
111 // Wait for the correct line...
114 // We have already passed the correct line -> It's empty...
115 return osg::Vec2(0, y);
118 // Next line and not returned -> not before any character
119 // -> return position after last character of line
120 return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
125 // Get threshold for mouse x position for setting cursor before or after
127 float threshold = coords[i * 4].x()
128 + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
133 if( i == 0 || line_numbers[i - 1] != line )
134 // first character of line
135 x = coords[i * 4].x();
136 else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
137 // If previous character width is zero set to begin of next character
138 // (Happens eg. with spaces)
139 x = coords[i * 4].x();
141 // position at center between characters
142 x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
144 return osg::Vec2(x, y);
148 // Nothing found -> return position after last character
152 (_lineCount - 0.5) * line_height
156 //----------------------------------------------------------------------------
157 osg::BoundingBox Text::TextOSG::computeBound() const
159 osg::BoundingBox bb = osgText::Text::computeBound();
163 // TODO bounding box still doesn't seem always right (eg. with center
164 // horizontal alignment not completely accurate)
165 bb._min.y() += _offset.y();
166 bb._max.y() += _offset.y();
168 _text_element->setBoundingBox(bb);
173 //----------------------------------------------------------------------------
174 Text::Text(SGPropertyNode_ptr node, const Style& parent_style):
175 Element(node, parent_style),
176 _text( new Text::TextOSG(this) )
179 _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
180 _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
181 _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
183 addStyle("fill", &TextOSG::setFill, _text);
184 addStyle("background", &TextOSG::setBackgroundColor, _text);
185 addStyle("character-size",
186 static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
188 addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
189 addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
192 // FILLEDBOUNDINGBOX = 4
194 addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
195 addStyle("max-width", &TextOSG::setMaximumWidth, _text);
196 addStyle("font", &Text::setFont, this);
197 addStyle("alignment", &Text::setAlignment, this);
198 addStyle("text", &Text::setText, this);
203 //----------------------------------------------------------------------------
209 //----------------------------------------------------------------------------
210 void Text::setText(const char* text)
212 _text->setText(text, osgText::String::ENCODING_UTF8);
215 //----------------------------------------------------------------------------
216 void Text::setFont(const char* name)
218 _text->setFont( getFont(name) );
221 //----------------------------------------------------------------------------
222 void Text::setAlignment(const char* align)
224 const std::string align_string(align);
226 #define ENUM_MAPPING(enum_val, string_val) \
227 else if( align_string == string_val )\
228 _text->setAlignment( osgText::Text::enum_val );
229 #include "text-alignment.hxx"
233 if( !align_string.empty() )
238 "canvas::Text: unknown alignment '" << align_string << "'"
240 _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
244 //----------------------------------------------------------------------------
246 const char* Text::getAlignment() const
248 switch( _text->getAlignment() )
250 #define ENUM_MAPPING(enum_val, string_val) \
251 case osgText::Text::enum_val:\
253 #include "text-alignment.hxx"
261 //----------------------------------------------------------------------------
262 void Text::childChanged(SGPropertyNode* child)
264 if( child->getParent() != _node )
267 const std::string& name = child->getNameString();
268 if( name == "hit-y" )
271 _node->getFloatValue("hit-x"),
272 _node->getFloatValue("hit-y")
276 //----------------------------------------------------------------------------
277 void Text::handleHit(float x, float y)
279 const osg::Vec2& pos = _text->handleHit(x, y);
280 _node->setFloatValue("cursor-x", pos.x());
281 _node->setFloatValue("cursor-y", pos.y());
284 //----------------------------------------------------------------------------
285 Text::font_ptr Text::getFont(const std::string& name)
287 SGPath path = globals->resolve_ressource_path("Fonts/" + name);
294 "canvas::Text: No such font: " << name
303 "canvas::Text: using font file " << path.str()
306 font_ptr font = osgText::readFontFile(path.c_str());
312 "canvas::Text: Failed to open font file " << path.c_str()
318 } // namespace canvas