1 #include <msp/gl/matrix.h>
2 #include <msp/gl/meshbuilder.h>
3 #include <msp/gl/texture2d.h>
13 struct Text::RenderData
15 GL::PrimitiveBuilder *bld;
18 struct Text::CoordsToGeomData
30 Text::Text(const Style &s, const string &t):
36 void Text::set_style(const Style *s)
42 const GL::Font &font = style->get_font();
43 float font_size = style->get_font_size();
44 for(vector<Line>::iterator i=lines.begin(); i!=lines.end(); ++i)
45 i->width = static_cast<unsigned>(font.get_string_width(text.substr(i->start, i->length))*font_size);
49 unsigned Text::get_width() const
52 for(vector<Line>::const_iterator i=lines.begin(); i!=lines.end(); ++i)
53 width = max(width, i->width);
57 unsigned Text::get_height() const
62 const GL::Font &font = style->get_font();
63 float font_size = style->get_font_size();
64 unsigned line_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font_size);
65 unsigned line_spacing = line_height*6/5;
66 return line_height+(lines.size()-1)*line_spacing;
69 void Text::autosize(const Part &part, Geometry &geom) const
71 const Sides &margin = part.get_margin();
72 geom.w = max(geom.w, get_width()+margin.left+margin.right);
73 geom.h = max(geom.h, get_height()+margin.top+margin.bottom);
76 void Text::set(const string &t)
82 void Text::erase(unsigned pos, unsigned len)
86 vector<Line>::iterator i;
87 for(i=lines.begin(); (i!=lines.end() && i->start+i->length<pos); ++i) ;
89 if(pos+len>i->start+i->length)
95 for(++i; i!=lines.end(); ++i)
100 void Text::insert(unsigned pos, const string &s)
104 if(s.find('\n')!=string::npos)
108 vector<Line>::iterator i;
109 for(i=lines.begin(); (i!=lines.end() && i->start+i->length<pos); ++i) ;
111 i->length += s.size();
113 for(++i; i!=lines.end(); ++i)
114 i->start += s.size();
118 unsigned Text::get_visible_lines(const Part &part, const Geometry &parent, unsigned *fit_height) const
120 const GL::Font &font = style->get_font();
121 float font_size = style->get_font_size();
122 unsigned line_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font_size);
123 unsigned line_spacing = static_cast<unsigned>(font_size*6/5);
125 const Sides &margin = part.get_margin();
126 unsigned vmargin = margin.top+margin.bottom;
127 unsigned free_height = max(parent.h, vmargin)-vmargin+line_spacing-line_height;
128 unsigned n_lines = min<unsigned>(lines.size(), max(free_height/line_spacing, 1U));
130 *fit_height = line_height+(n_lines-1)*line_spacing;
135 unsigned Text::get_line_length(unsigned i) const
138 throw out_of_range("Text::get_line_length");
139 return lines[i].length;
142 void Text::offset_to_coords(unsigned offs, unsigned &row, unsigned &col) const
151 for(unsigned i=0; i<lines.size(); ++i)
152 if(offs>=lines[i].start && offs<=lines[i].start+lines[i].length)
155 col = offs-lines[i].start;
160 unsigned Text::coords_to_offset(unsigned row, unsigned col) const
162 if(row>=lines.size())
165 return lines[row].start+min(col, lines[row].length);
168 Geometry Text::coords_to_geometry(const Part &part, const Geometry &parent, unsigned first_row, unsigned row, unsigned col) const
170 if(row>=lines.size())
171 row = lines.size()-1;
172 const Line &line = lines[row];
176 CoordsToGeomData data;
180 process_lines(part, parent, first_row, &Text::coords_to_geom_line, data);
185 void Text::build(const Part &part, State state, const Geometry &parent, PartCache &cache) const
187 build(part, state, parent, 0, cache);
190 void Text::build(const Part &part, State state, const Geometry &parent, unsigned first_row, PartCache &cache) const
192 if(!style || lines.empty())
195 const GL::Font &font = style->get_font();
196 GL::MeshBuilder bld(cache.create_mesh(part, font.get_texture()));
197 bld.color(style->get_font_color(state));
202 process_lines(part, parent, first_row, &Text::build_line, data);
205 Text &Text::operator=(const string &t)
211 void Text::find_lines()
214 float font_size = (style ? style->get_font_size() : 1);
215 string::size_type start = 0;
218 string::size_type newline = text.find('\n', start);
222 line.length = (newline==string::npos ? text.size() : newline)-start;
223 line.width = line.length;
226 string str = text.substr(line.start, line.length);
227 line.width = static_cast<unsigned>(style->get_font().get_string_width(str)*font_size);
229 lines.push_back(line);
231 if(newline==string::npos)
238 void Text::process_lines(const Part &part, const Geometry &parent, unsigned first_row, void (Text::*func)(unsigned, const Geometry &, T &) const, T &data) const
243 const GL::Font &font = style->get_font();
244 float font_size = style->get_font_size();
245 unsigned line_spacing = static_cast<unsigned>(font_size*6/5);
246 int y_offset = static_cast<int>(-font.get_descent()*font_size);
249 unsigned n_lines = get_visible_lines(part, parent, &fit_height);
250 first_row = min<unsigned>(first_row, lines.size()-n_lines);
252 for(unsigned i=0; i<n_lines; ++i)
254 const Line &line = lines[first_row+i];
257 rgeom.w = line.width;
258 rgeom.h = fit_height;
259 rgeom.y = (n_lines-1-i)*line_spacing+y_offset;
260 part.get_alignment().apply(rgeom, parent, part.get_margin());
262 (this->*func)(first_row+i, rgeom, data);
266 void Text::build_line(unsigned i, const Geometry &rgeom, RenderData &data) const
268 const Line &line = lines[i];
270 GL::MatrixStack::Push _pushm(data.bld->matrix());
271 data.bld->matrix() *= GL::Matrix::translation(rgeom.x, rgeom.y, 0);
272 data.bld->matrix() *= GL::Matrix::scaling(style->get_font_size());
274 style->get_font().build_string(text.substr(line.start, line.length), *data.bld);
277 void Text::coords_to_geom_line(unsigned i, const Geometry &rgeom, CoordsToGeomData &data) const
281 float w = style->get_font().get_string_width(text.substr(lines[i].start, data.col));
283 data.result.x += static_cast<unsigned>(w*style->get_font_size());