]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasText.cxx
3e5d78531cf71c3bfff0124c032894cd8b3b756c
[simgear.git] / simgear / canvas / elements / CanvasText.cxx
1 // A text on the Canvas
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
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.
9 //
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.
14 //
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
18
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>
24
25 namespace simgear
26 {
27 namespace canvas
28 {
29   class Text::TextOSG:
30     public osgText::Text
31   {
32     public:
33
34       TextOSG(canvas::Text* text);
35
36       void setCharacterAspect(float aspect);
37       void setFill(const std::string& fill);
38       void setBackgroundColor(const std::string& fill);
39
40       osg::Vec2 handleHit(float x, float y);
41
42       virtual osg::BoundingBox computeBound() const;
43
44     protected:
45
46       canvas::Text *_text_element;
47   };
48
49   //----------------------------------------------------------------------------
50   Text::TextOSG::TextOSG(canvas::Text* text):
51     _text_element(text)
52   {
53
54   }
55
56   //----------------------------------------------------------------------------
57   void Text::TextOSG::setCharacterAspect(float aspect)
58   {
59     setCharacterSize(getCharacterHeight(), aspect);
60   }
61
62   //----------------------------------------------------------------------------
63   void Text::TextOSG::setFill(const std::string& fill)
64   {
65 //    if( fill == "none" )
66 //      TODO No text
67 //    else
68     osg::Vec4 color;
69     if( parseColor(fill, color) )
70       setColor( color );
71   }
72
73   //----------------------------------------------------------------------------
74   void Text::TextOSG::setBackgroundColor(const std::string& fill)
75   {
76     osg::Vec4 color;
77     if( parseColor(fill, color) )
78       setBoundingBoxColor( color );
79   }
80
81   //----------------------------------------------------------------------------
82   osg::Vec2 Text::TextOSG::handleHit(float x, float y)
83   {
84     float line_height = _characterHeight + _lineSpacing;
85
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, (y - first_line_y) / line_height);
89
90     if( _textureGlyphQuadMap.empty() )
91       return osg::Vec2(-1, -1);
92
93     // TODO check when it can be larger
94     assert( _textureGlyphQuadMap.size() == 1 );
95
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;
100
101     const float HIT_FRACTION = 0.6;
102     const float character_width = getCharacterHeight()
103                                 * getCharacterAspectRatio();
104
105     y = (line + 0.5) * line_height;
106
107     bool line_found = false;
108     for(size_t i = 0; i < line_numbers.size(); ++i)
109     {
110       if( line_numbers[i] != line )
111       {
112         if( !line_found )
113         {
114           if( line_numbers[i] < line )
115             // Wait for the correct line...
116             continue;
117
118           // We have already passed the correct line -> It's empty...
119           return osg::Vec2(0, y);
120         }
121
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);
125       }
126
127       line_found = true;
128
129       // Get threshold for mouse x position for setting cursor before or after
130       // current character
131       float threshold = coords[i * 4].x()
132                       + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
133                                      * character_width;
134
135       if( x <= threshold )
136       {
137         if( i == 0 || line_numbers[i - 1] != line )
138           // first character of line
139           x = coords[i * 4].x();
140         else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
141           // If previous character width is zero set to begin of next character
142           // (Happens eg. with spaces)
143           x = coords[i * 4].x();
144         else
145           // position at center between characters
146           x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
147
148         return osg::Vec2(x, y);
149       }
150     }
151
152     // Nothing found -> return position after last character
153     return osg::Vec2
154     (
155       coords.back().x(),
156       (_lineCount - 0.5) * line_height
157     );
158   }
159
160   //----------------------------------------------------------------------------
161   osg::BoundingBox Text::TextOSG::computeBound() const
162   {
163     osg::BoundingBox bb = osgText::Text::computeBound();
164     if( !bb.valid() )
165       return bb;
166
167     // TODO bounding box still doesn't seem always right (eg. with center
168     //      horizontal alignment not completely accurate)
169     bb._min.y() += _offset.y();
170     bb._max.y() += _offset.y();
171
172     _text_element->setBoundingBox(bb);
173
174     return bb;
175   }
176
177   //----------------------------------------------------------------------------
178   Text::Text( const CanvasWeakPtr& canvas,
179               const SGPropertyNode_ptr& node,
180               const Style& parent_style,
181               Element* parent ):
182     Element(canvas, node, parent_style, parent),
183     _text( new Text::TextOSG(this) )
184   {
185     setDrawable(_text);
186     _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
187     _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
188     _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
189
190     addStyle("fill", &TextOSG::setFill, _text);
191     addStyle("background", &TextOSG::setBackgroundColor, _text);
192     addStyle("character-size",
193              static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
194              _text);
195     addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
196     addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
197     //  TEXT              = 1 default
198     //  BOUNDINGBOX       = 2
199     //  FILLEDBOUNDINGBOX = 4
200     //  ALIGNMENT         = 8
201     addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
202     addStyle("max-width", &TextOSG::setMaximumWidth, _text);
203     addStyle("font", &Text::setFont, this);
204     addStyle("alignment", &Text::setAlignment, this);
205     addStyle("text", &Text::setText, this);
206
207     setupStyle();
208   }
209
210   //----------------------------------------------------------------------------
211   Text::~Text()
212   {
213
214   }
215
216   //----------------------------------------------------------------------------
217   void Text::setText(const char* text)
218   {
219     _text->setText(text, osgText::String::ENCODING_UTF8);
220   }
221
222   //----------------------------------------------------------------------------
223   void Text::setFont(const char* name)
224   {
225     _text->setFont( _canvas.lock()->getSystemAdapter()->getFont(name) );
226   }
227
228   //----------------------------------------------------------------------------
229   void Text::setAlignment(const char* align)
230   {
231     const std::string align_string(align);
232     if( 0 ) return;
233 #define ENUM_MAPPING(enum_val, string_val) \
234     else if( align_string == string_val )\
235       _text->setAlignment( osgText::Text::enum_val );
236 #include "text-alignment.hxx"
237 #undef ENUM_MAPPING
238     else
239     {
240       if( !align_string.empty() )
241         SG_LOG
242         (
243           SG_GENERAL,
244           SG_WARN,
245           "canvas::Text: unknown alignment '" << align_string << "'"
246         );
247       _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
248     }
249   }
250
251   //----------------------------------------------------------------------------
252 #if 0
253   const char* Text::getAlignment() const
254   {
255     switch( _text->getAlignment() )
256     {
257 #define ENUM_MAPPING(enum_val, string_val) \
258       case osgText::Text::enum_val:\
259         return string_val;
260 #include "text-alignment.hxx"
261 #undef ENUM_MAPPING
262       default:
263         return "unknown";
264     }
265   }
266 #endif
267
268   //----------------------------------------------------------------------------
269   void Text::childChanged(SGPropertyNode* child)
270   {
271     if( child->getParent() != _node )
272       return;
273
274     const std::string& name = child->getNameString();
275     if( name == "hit-y" )
276       handleHit
277       (
278         _node->getFloatValue("hit-x"),
279         _node->getFloatValue("hit-y")
280       );
281   }
282
283   //----------------------------------------------------------------------------
284   void Text::handleHit(float x, float y)
285   {
286     const osg::Vec2& pos = _text->handleHit(x, y);
287     _node->setFloatValue("cursor-x", pos.x());
288     _node->setFloatValue("cursor-y", pos.y());
289   }
290
291 } // namespace canvas
292 } // namespace simgear