1 // A text on the Canvas
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Library General Public License for more details.
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "CanvasText.hxx"
20 #include <simgear/canvas/Canvas.hxx>
21 #include <simgear/canvas/CanvasSystemAdapter.hxx>
22 #include <simgear/scene/util/parse_color.hxx>
23 #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(const osg::Vec2f& pos);
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" )
69 if( parseColor(fill, color) )
73 //----------------------------------------------------------------------------
74 void Text::TextOSG::setBackgroundColor(const std::string& fill)
77 if( parseColor(fill, color) )
78 setBoundingBoxColor( color );
81 //----------------------------------------------------------------------------
82 osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
84 float line_height = _characterHeight + _lineSpacing;
86 // TODO check with align other than TOP
87 float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
88 size_t line = std::max<int>(0, (pos.y() - first_line_y) / line_height);
90 if( _textureGlyphQuadMap.empty() )
91 return osg::Vec2(-1, -1);
93 // TODO check when it can be larger
94 assert( _textureGlyphQuadMap.size() == 1 );
96 const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
97 const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
98 const GlyphQuads::Coords2& coords = glyphquad._coords;
99 const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
101 const float HIT_FRACTION = 0.6;
102 const float character_width = getCharacterHeight()
103 * getCharacterAspectRatio();
105 float y = (line + 0.5) * line_height;
107 bool line_found = false;
108 for(size_t i = 0; i < line_numbers.size(); ++i)
110 if( line_numbers[i] != line )
114 if( line_numbers[i] < line )
115 // Wait for the correct line...
118 // We have already passed the correct line -> It's empty...
119 return osg::Vec2(0, y);
122 // Next line and not returned -> not before any character
123 // -> return position after last character of line
124 return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
129 // Get threshold for mouse x position for setting cursor before or after
131 float threshold = coords[i * 4].x()
132 + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
135 if( pos.x() <= threshold )
138 if( i == 0 || line_numbers[i - 1] != line )
139 // first character of line
140 hit.x() = coords[i * 4].x();
141 else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
142 // If previous character width is zero set to begin of next character
143 // (Happens eg. with spaces)
144 hit.x() = coords[i * 4].x();
146 // position at center between characters
147 hit.x() = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
153 // Nothing found -> return position after last character
157 (_lineCount - 0.5) * line_height
161 //----------------------------------------------------------------------------
162 osg::BoundingBox Text::TextOSG::computeBound() const
164 osg::BoundingBox bb = osgText::Text::computeBound();
168 // TODO bounding box still doesn't seem always right (eg. with center
169 // horizontal alignment not completely accurate)
170 bb._min.y() += _offset.y();
171 bb._max.y() += _offset.y();
173 _text_element->setBoundingBox(bb);
178 //----------------------------------------------------------------------------
179 Text::Text( const CanvasWeakPtr& canvas,
180 const SGPropertyNode_ptr& node,
181 const Style& parent_style,
183 Element(canvas, node, parent_style, parent),
184 _text( new Text::TextOSG(this) )
187 _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
188 _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
189 _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
191 addStyle("fill", &TextOSG::setFill, _text);
192 addStyle("background", &TextOSG::setBackgroundColor, _text);
193 addStyle("character-size",
194 static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
196 addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
197 addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
200 // FILLEDBOUNDINGBOX = 4
202 addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
203 addStyle("max-width", &TextOSG::setMaximumWidth, _text);
204 addStyle("font", &Text::setFont, this);
205 addStyle("alignment", &Text::setAlignment, this);
206 addStyle("text", &Text::setText, this);
211 //----------------------------------------------------------------------------
217 //----------------------------------------------------------------------------
218 void Text::setText(const char* text)
220 _text->setText(text, osgText::String::ENCODING_UTF8);
223 //----------------------------------------------------------------------------
224 void Text::setFont(const char* name)
226 _text->setFont( _canvas.lock()->getSystemAdapter()->getFont(name) );
229 //----------------------------------------------------------------------------
230 void Text::setAlignment(const char* align)
232 const std::string align_string(align);
234 #define ENUM_MAPPING(enum_val, string_val) \
235 else if( align_string == string_val )\
236 _text->setAlignment( osgText::Text::enum_val );
237 #include "text-alignment.hxx"
241 if( !align_string.empty() )
246 "canvas::Text: unknown alignment '" << align_string << "'"
248 _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
252 //----------------------------------------------------------------------------
254 const char* Text::getAlignment() const
256 switch( _text->getAlignment() )
258 #define ENUM_MAPPING(enum_val, string_val) \
259 case osgText::Text::enum_val:\
261 #include "text-alignment.hxx"
269 //----------------------------------------------------------------------------
270 osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
272 return _text->handleHit(pos);
275 } // namespace canvas
276 } // namespace simgear