]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/elements/text.cxx
Helpers to access view position.
[flightgear.git] / src / Canvas / elements / text.cxx
1 // A text on the canvas
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
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.
9 //
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.
14 //
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.
18
19 #include "text.hxx"
20 #include <Canvas/property_helper.hxx>
21
22 #include <Main/globals.hxx>
23 #include <Main/fg_props.hxx>
24
25 #include <osgText/Text>
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       setColor( parseColor(fill) );
69   }
70
71   //----------------------------------------------------------------------------
72   void Text::TextOSG::setBackgroundColor(const std::string& fill)
73   {
74     setBoundingBoxColor( parseColor(fill) );
75   }
76
77   //----------------------------------------------------------------------------
78   osg::Vec2 Text::TextOSG::handleHit(float x, float y)
79   {
80     float line_height = _characterHeight + _lineSpacing;
81
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);
85
86     if( _textureGlyphQuadMap.empty() )
87       return osg::Vec2(-1, -1);
88
89     // TODO check when it can be larger
90     assert( _textureGlyphQuadMap.size() == 1 );
91
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;
96
97     const float HIT_FRACTION = 0.6;
98     const float character_width = getCharacterHeight()
99                                 * getCharacterAspectRatio();
100
101     y = (line + 0.5) * line_height;
102
103     bool line_found = false;
104     for(size_t i = 0; i < line_numbers.size(); ++i)
105     {
106       if( line_numbers[i] != line )
107       {
108         if( !line_found )
109         {
110           if( line_numbers[i] < line )
111             // Wait for the correct line...
112             continue;
113
114           // We have already passed the correct line -> It's empty...
115           return osg::Vec2(0, y);
116         }
117
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);
121       }
122
123       line_found = true;
124
125       // Get threshold for mouse x position for setting cursor before or after
126       // current character
127       float threshold = coords[i * 4].x()
128                       + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
129                                      * character_width;
130
131       if( x <= threshold )
132       {
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();
140         else
141           // position at center between characters
142           x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
143
144         return osg::Vec2(x, y);
145       }
146     }
147
148     // Nothing found -> return position after last character
149     return osg::Vec2
150     (
151       coords.back().x(),
152       (_lineCount - 0.5) * line_height
153     );
154   }
155
156   //----------------------------------------------------------------------------
157   osg::BoundingBox Text::TextOSG::computeBound() const
158   {
159     osg::BoundingBox bb = osgText::Text::computeBound();
160     if( !bb.valid() )
161       return bb;
162
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();
167
168     _text_element->setBoundingBox(bb);
169
170     return bb;
171   }
172
173   //----------------------------------------------------------------------------
174   Text::Text(SGPropertyNode_ptr node, const Style& parent_style):
175     Element(node, parent_style),
176     _text( new Text::TextOSG(this) )
177   {
178     setDrawable(_text);
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));
182
183     addStyle("fill", &TextOSG::setFill, _text);
184     addStyle("background", &TextOSG::setBackgroundColor, _text);
185     addStyle("character-size",
186              static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
187              _text);
188     addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
189     addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
190     //  TEXT              = 1 default
191     //  BOUNDINGBOX       = 2
192     //  FILLEDBOUNDINGBOX = 4
193     //  ALIGNMENT         = 8
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);
199
200     setupStyle();
201   }
202
203   //----------------------------------------------------------------------------
204   Text::~Text()
205   {
206
207   }
208
209   //----------------------------------------------------------------------------
210   void Text::setText(const char* text)
211   {
212     _text->setText(text, osgText::String::ENCODING_UTF8);
213   }
214
215   //----------------------------------------------------------------------------
216   void Text::setFont(const char* name)
217   {
218     _text->setFont( getFont(name) );
219   }
220
221   //----------------------------------------------------------------------------
222   void Text::setAlignment(const char* align)
223   {
224     const std::string align_string(align);
225     if( 0 ) return;
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"
230 #undef ENUM_MAPPING
231     else
232     {
233       if( !align_string.empty() )
234         SG_LOG
235         (
236           SG_GENERAL,
237           SG_WARN,
238           "canvas::Text: unknown alignment '" << align_string << "'"
239         );
240       _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
241     }
242   }
243
244   //----------------------------------------------------------------------------
245 #if 0
246   const char* Text::getAlignment() const
247   {
248     switch( _text->getAlignment() )
249     {
250 #define ENUM_MAPPING(enum_val, string_val) \
251       case osgText::Text::enum_val:\
252         return string_val;
253 #include "text-alignment.hxx"
254 #undef ENUM_MAPPING
255       default:
256         return "unknown";
257     }
258   }
259 #endif
260
261   //----------------------------------------------------------------------------
262   void Text::childChanged(SGPropertyNode* child)
263   {
264     if( child->getParent() != _node )
265       return;
266
267     const std::string& name = child->getNameString();
268     if( name == "hit-y" )
269       handleHit
270       (
271         _node->getFloatValue("hit-x"),
272         _node->getFloatValue("hit-y")
273       );
274   }
275
276   //----------------------------------------------------------------------------
277   void Text::handleHit(float x, float y)
278   {
279     const osg::Vec2& pos = _text->handleHit(x, y);
280     _node->setFloatValue("cursor-x", pos.x());
281     _node->setFloatValue("cursor-y", pos.y());
282   }
283
284   //----------------------------------------------------------------------------
285   Text::font_ptr Text::getFont(const std::string& name)
286   {
287     SGPath path = globals->resolve_ressource_path("Fonts/" + name);
288     if( path.isNull() )
289     {
290       SG_LOG
291       (
292         SG_GL,
293         SG_ALERT,
294         "canvas::Text: No such font: " << name
295       );
296       return font_ptr();
297     }
298
299     SG_LOG
300     (
301       SG_GL,
302       SG_INFO,
303       "canvas::Text: using font file " << path.str()
304     );
305
306     font_ptr font = osgText::readFontFile(path.c_str());
307     if( !font )
308       SG_LOG
309       (
310         SG_GL,
311         SG_ALERT,
312         "canvas::Text: Failed to open font file " << path.c_str()
313       );
314
315     return font;
316   }
317
318 } // namespace canvas