]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/elements/text.cxx
Canvas: First version of new Canvas GUI system.
[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       osg::Vec2 handleHit(float x, float y);
34   };
35
36   //----------------------------------------------------------------------------
37   osg::Vec2 Text::TextOSG::handleHit(float x, float y)
38   {
39     float line_height = _characterHeight + _lineSpacing;
40
41     // TODO check with align other than TOP
42     float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
43     size_t line = std::max<int>(0, (y - first_line_y) / line_height);
44
45     if( _textureGlyphQuadMap.empty() )
46       return osg::Vec2(-1, -1);
47
48     // TODO check when it can be larger
49     assert( _textureGlyphQuadMap.size() == 1 );
50
51     const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
52     const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
53     const GlyphQuads::Coords2& coords = glyphquad._coords;
54     const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
55
56     const float HIT_FRACTION = 0.6;
57     const float character_width = getCharacterHeight()
58                                 * getCharacterAspectRatio();
59
60     y = (line + 0.5) * line_height;
61
62     bool line_found = false;
63     for(size_t i = 0; i < line_numbers.size(); ++i)
64     {
65       if( line_numbers[i] != line )
66       {
67         if( !line_found )
68         {
69           if( line_numbers[i] < line )
70             // Wait for the correct line...
71             continue;
72
73           // We have already passed the correct line -> It's empty...
74           return osg::Vec2(0, y);
75         }
76
77         // Next line and not returned -> not before any character
78         // -> return position after last character of line
79         return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
80       }
81
82       line_found = true;
83
84       // Get threshold for mouse x position for setting cursor before or after
85       // current character
86       float threshold = coords[i * 4].x()
87                       + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
88                                      * character_width;
89
90       if( x <= threshold )
91       {
92         if( i == 0 || line_numbers[i - 1] != line )
93           // first character of line
94           x = coords[i * 4].x();
95         else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
96           // If previous character width is zero set to begin of next character
97           // (Happens eg. with spaces)
98           x = coords[i * 4].x();
99         else
100           // position at center between characters
101           x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
102
103         return osg::Vec2(x, y);
104       }
105     }
106
107     // Nothing found -> return position after last character
108     return osg::Vec2
109     (
110       coords.back().x(),
111       (_lineCount - 0.5) * line_height
112     );
113   }
114
115   //----------------------------------------------------------------------------
116   Text::Text(SGPropertyNode_ptr node):
117     Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
118     _text( new Text::TextOSG() ),
119     _font_size( 0 ),
120     _font_aspect( 0 )
121   {
122     setDrawable(_text);
123     _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
124     _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
125     _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
126
127     _font_size = getChildDefault<float>(_node, "character-size", 32);
128     _font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1);
129   }
130
131   //----------------------------------------------------------------------------
132   Text::~Text()
133   {
134
135   }
136
137   //----------------------------------------------------------------------------
138   void Text::update(double dt)
139   {
140     Element::update(dt);
141
142     if( _attributes_dirty & FONT_SIZE )
143     {
144       _text->setCharacterSize
145       (
146         _font_size->getFloatValue(),
147         _font_aspect->getFloatValue()
148       );
149
150       _attributes_dirty &= ~FONT_SIZE;
151     }
152   }
153
154   //----------------------------------------------------------------------------
155   void Text::setFont(const char* name)
156   {
157     _text->setFont( getFont(name) );
158   }
159
160   //----------------------------------------------------------------------------
161   void Text::setAlignment(const char* align)
162   {
163     const std::string align_string(align);
164     if( 0 ) return;
165 #define ENUM_MAPPING(enum_val, string_val) \
166     else if( align_string == string_val )\
167       _text->setAlignment( osgText::Text::enum_val );
168 #include "text-alignment.hxx"
169 #undef ENUM_MAPPING
170     else
171     {
172       if( !align_string.empty() )
173         SG_LOG
174         (
175           SG_GENERAL,
176           SG_WARN,
177           "canvas::Text: unknown alignment '" << align_string << "'"
178         );
179       _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
180     }
181   }
182
183   //----------------------------------------------------------------------------
184 #if 0
185   const char* Text::getAlignment() const
186   {
187     switch( _text->getAlignment() )
188     {
189 #define ENUM_MAPPING(enum_val, string_val) \
190       case osgText::Text::enum_val:\
191         return string_val;
192 #include "text-alignment.hxx"
193 #undef ENUM_MAPPING
194       default:
195         return "unknown";
196     }
197   }
198 #endif
199   //----------------------------------------------------------------------------
200   void Text::childChanged(SGPropertyNode* child)
201   {
202     const std::string& name = child->getNameString();
203
204     if( name == "hit-y" )
205       handleHit
206       (
207         _node->getFloatValue("hit-x"),
208         _node->getFloatValue("hit-y")
209       );
210     else if( _font_size == child || _font_aspect == child )
211       _attributes_dirty |= FONT_SIZE;
212     else if( name == "text" )
213       _text->setText
214       (
215         osgText::String( child->getStringValue(),
216                          osgText::String::ENCODING_UTF8 )
217       );
218     else if( name == "padding" )
219       _text->setBoundingBoxMargin( child->getFloatValue() );
220     else if( name == "draw-mode" )
221       //  TEXT              = 1 default
222       //  BOUNDINGBOX       = 2
223       //  FILLEDBOUNDINGBOX = 4
224       //  ALIGNMENT         = 8
225       _text->setDrawMode( child->getIntValue() );
226     else if( name == "max-width" )
227       _text->setMaximumWidth( child->getFloatValue() );
228     else if( name == "font" )
229       setFont( child->getStringValue() );
230     else if( name == "alignment" )
231       setAlignment( child->getStringValue() );
232   }
233
234   //----------------------------------------------------------------------------
235   void Text::colorChanged(const osg::Vec4& color)
236   {
237     _text->setColor(color);
238   }
239
240   //----------------------------------------------------------------------------
241   void Text::colorFillChanged(const osg::Vec4& color)
242   {
243     _text->setBoundingBoxColor(color);
244   }
245
246   //----------------------------------------------------------------------------
247   void Text::handleHit(float x, float y)
248   {
249     const osg::Vec2& pos = _text->handleHit(x, y);
250     _node->setFloatValue("cursor-x", pos.x());
251     _node->setFloatValue("cursor-y", pos.y());
252   }
253
254   //----------------------------------------------------------------------------
255   Text::font_ptr Text::getFont(const std::string& name)
256   {
257     SGPath path = globals->resolve_ressource_path("Fonts/" + name);
258     if( path.isNull() )
259     {
260       SG_LOG
261       (
262         SG_GL,
263         SG_ALERT,
264         "canvas::Text: No such font: " << name
265       );
266       return font_ptr();
267     }
268
269     SG_LOG
270     (
271       SG_GL,
272       SG_INFO,
273       "canvas::Text: using font file " << path.str()
274     );
275
276     font_ptr font = osgText::readFontFile(path.c_str());
277     if( !font )
278       SG_LOG
279       (
280         SG_GL,
281         SG_ALERT,
282         "canvas::Text: Failed to open font file " << path.c_str()
283       );
284
285     return font;
286   }
287
288 } // namespace canvas