From 70c409d5ae005b96f89d47da49f868e65b4e5734 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 6 Oct 2010 12:28:47 +0000 Subject: [PATCH] Add a VSlider to multiline Entries Make Text only render as many lines as fit in the widget --- source/entry.cpp | 102 +++++++++++++++++++++++++++++++++++++++--- source/entry.h | 14 +++++- source/text.cpp | 112 +++++++++++++++++++++++++++++++---------------- source/text.h | 13 +++++- 4 files changed, 196 insertions(+), 45 deletions(-) diff --git a/source/entry.cpp b/source/entry.cpp index 91bd9c0..654ac6c 100644 --- a/source/entry.cpp +++ b/source/entry.cpp @@ -13,6 +13,7 @@ Distributed under the LGPL #include "graphic.h" #include "part.h" #include "style.h" +#include "vslider.h" using namespace std; @@ -21,9 +22,13 @@ namespace GLtk { Entry::Entry(const Resources &r, const string &t): Widget(r), + Container(r), text(), multiline(false), - edit_pos(0) + edit_pos(0), + first_row(0), + text_part(0), + slider(0) { update_style(); set_text(t); @@ -38,6 +43,14 @@ void Entry::set_text(const string &t) void Entry::set_multiline(bool m) { multiline = m; + if(multiline && !slider) + { + slider = new VSlider(res); + add(*slider); + slider->set_step(1); + slider->signal_value_changed.connect(sigc::mem_fun(this, &Entry::slider_value_changed)); + reposition_slider(); + } } void Entry::key_press(unsigned key, unsigned, wchar_t ch) @@ -45,37 +58,53 @@ void Entry::key_press(unsigned key, unsigned, wchar_t ch) if(key==Input::KEY_LEFT) { if(edit_pos>0) + { --edit_pos; + check_view_range(); + } } else if(key==Input::KEY_RIGHT) { if(edit_pos0) + { edit_pos = text.coords_to_offset(row-1, col); + check_view_range(); + } else edit_pos = 0; } else if(key==Input::KEY_BACKSPACE) { if(edit_pos>0) + { text.erase(--edit_pos, 1); + check_view_range(); + } } else if(key==Input::KEY_ENTER) { if(multiline) + { text.insert(edit_pos++, "\n"); + check_view_range(); + } else signal_enter.emit(); } @@ -89,28 +118,91 @@ void Entry::key_press(unsigned key, unsigned, wchar_t ch) void Entry::render_special(const Part &part) const { if(part.get_name()=="text") - text.render(part, geom); + text.render(part, geom, first_row); else if(part.get_name()=="cursor") { - if(!part.get_graphic(state)) + if(!text_part || !part.get_graphic(state)) return; unsigned row, col; text.offset_to_coords(edit_pos, row, col); - Geometry rgeom = text.coords_to_geometry(row, col); - part.get_alignment().apply(rgeom, geom, part.get_margin()); + if(row=first_row+visible_rows) + return; + + Geometry rgeom = text.coords_to_geometry(*text_part, geom, first_row, row, col); GL::push_matrix(); GL::translate(rgeom.x, rgeom.y, 0); part.get_graphic(state)->render(part.get_geometry().w, part.get_geometry().h); GL::pop_matrix(); } + else if(part.get_name()=="slider") + slider->render(); +} + +void Entry::on_geometry_change() +{ + reposition_slider(); } void Entry::on_style_change() { + text_part = 0; + for(list::const_iterator i=style->get_parts().begin(); i!=style->get_parts().end(); ++i) + if(i->get_name()=="text") + text_part = &*i; + text.set_style(style); + reposition_slider(); +} + +void Entry::reposition_slider() +{ + if(!slider) + return; + + for(list::const_iterator i=style->get_parts().begin(); i!=style->get_parts().end(); ++i) + if(i->get_name()=="slider") + { + Geometry sgeom = i->get_geometry(); + i->get_alignment().apply(sgeom, geom, i->get_margin()); + slider->set_geometry(sgeom); + } +} + +void Entry::slider_value_changed(double value) +{ + if(text.get_n_lines()>visible_rows) + first_row = text.get_n_lines()-visible_rows-static_cast(value); +} + +void Entry::check_view_range() +{ + if(!multiline || !text_part) + return; + + const GL::Font *font = style->get_font(); + float font_size = font->get_default_size(); + unsigned line_spacing = static_cast(font_size*6/5); + + const Sides &margin = text_part->get_margin(); + visible_rows = (geom.h-margin.top-margin.bottom)/line_spacing; + + unsigned row, col; + text.offset_to_coords(edit_pos, row, col); + + if(first_row>row) + first_row = row; + else if(row>=first_row+visible_rows) + first_row = row+1-visible_rows; + + if(slider) + { + unsigned scroll = max(text.get_n_lines(), visible_rows)-visible_rows; + slider->set_range(0, scroll); + slider->set_value(scroll-first_row); + } } diff --git a/source/entry.h b/source/entry.h index 0ff5466..a8bbcd5 100644 --- a/source/entry.h +++ b/source/entry.h @@ -8,12 +8,15 @@ Distributed under the LGPL #ifndef MSP_GLTK_ENTRY_H_ #define MSP_GLTK_ENTRY_H_ +#include "container.h" #include "text.h" #include "widget.h" namespace Msp { namespace GLtk { +class VSlider; + /** Text entry field. @@ -21,8 +24,9 @@ Special parts: text The current text of the widget. Graphics are ignored. cursor Indicates the current input position. Fill_x is ignored. + slider A vertical slider for multiline entries. */ -class Entry: public Widget +class Entry: virtual public Widget, private Container { public: class Loader: public Widget::Loader @@ -35,6 +39,10 @@ private: Text text; bool multiline; unsigned edit_pos; + unsigned first_row; + unsigned visible_rows; + const Part *text_part; + VSlider *slider; public: sigc::signal signal_enter; @@ -51,7 +59,11 @@ private: virtual const char *get_class() const { return "entry"; } virtual void render_special(const Part &) const; + virtual void on_geometry_change(); virtual void on_style_change(); + void reposition_slider(); + void slider_value_changed(double); + void check_view_range(); }; } // namespace GLtk diff --git a/source/text.cpp b/source/text.cpp index 5fc546c..1b46512 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -15,6 +15,19 @@ using namespace std; namespace Msp { namespace GLtk { +struct Text::RenderData +{ + GL::PrimitiveBuilder *bld; +}; + +struct Text::CoordsToGeomData +{ + unsigned row; + unsigned col; + Geometry result; +}; + + Text::Text(): style(0) { } @@ -126,7 +139,7 @@ unsigned Text::coords_to_offset(unsigned row, unsigned col) const return lines[row].start+min(col, lines[row].length); } -Geometry Text::coords_to_geometry(unsigned row, unsigned col) const +Geometry Text::coords_to_geometry(const Part &part, const Geometry &parent, unsigned first_row, unsigned row, unsigned col) const { if(row>=lines.size()) row = lines.size()-1; @@ -134,55 +147,28 @@ Geometry Text::coords_to_geometry(unsigned row, unsigned col) const if(col>line.length) col = line.length; - const GL::Font *font = style->get_font(); - float font_size = font->get_default_size(); - unsigned line_height = static_cast((font->get_ascent()-font->get_descent())*font_size); - unsigned line_spacing = static_cast(font_size*6/5); - unsigned height = line_height+(lines.size()-1)*line_spacing; - int y_offset = static_cast(-font->get_descent()*font_size); + CoordsToGeomData data; + data.row = row; + data.col = col; - Geometry geom; - geom.w = line.width; - geom.h = height; - geom.x = static_cast(font->get_string_width(text.substr(line.start, col))*font_size); - geom.y = (lines.size()-1-row)*line_spacing+y_offset; + process_lines(part, parent, first_row, data); - return geom; + return data.result; } -void Text::render(const Part &part, const Geometry &geom) const +void Text::render(const Part &part, const Geometry &parent, unsigned first_row) const { if(lines.empty()) return; - const GL::Font *font = style->get_font(); - float font_size = font->get_default_size(); - unsigned line_height = static_cast((font->get_ascent()-font->get_descent())*font_size); - unsigned line_spacing = static_cast(font_size*6/5); - unsigned height = line_height+(lines.size()-1)*line_spacing; - int y_offset = static_cast(-font->get_descent()*font_size); - const GL::Color &color = style->get_font_color(); GL::Immediate imm((GL::COLOR4_UBYTE, GL::TEXCOORD2, GL::VERTEX2)); imm.color(color.r, color.g, color.b); - for(unsigned i=0; idraw_string(text.substr(line.start, line.length), imm); + RenderData data; + data.bld = &imm; - GL::pop_matrix(); - } + process_lines(part, parent, first_row, data); } Text &Text::operator=(const string &t) @@ -212,5 +198,57 @@ void Text::find_lines() } } +template +void Text::process_lines(const Part &part, const Geometry &parent, unsigned first_row, T &data) const +{ + const GL::Font *font = style->get_font(); + float font_size = font->get_default_size(); + unsigned line_height = static_cast((font->get_ascent()-font->get_descent())*font_size); + unsigned line_spacing = static_cast(font_size*6/5); + unsigned height = line_height+(lines.size()-1)*line_spacing; + int y_offset = static_cast(-font->get_descent()*font_size); + + const Sides &margin = part.get_margin(); + unsigned n_lines = min(lines.size(), (parent.h-margin.top-margin.bottom)/line_spacing); + first_row = min(first_row, lines.size()-n_lines); + + for(unsigned i=0; i*func)(first_row+i, rgeom, data); + } +} + +void Text::render_line(unsigned i, const Geometry &rgeom, RenderData &data) const +{ + const Line &line = lines[i]; + const GL::Font *font = style->get_font(); + + GL::PushMatrix _pushm; + GL::translate(rgeom.x, rgeom.y, 0); + GL::scale_uniform(font->get_default_size()); + + font->draw_string(text.substr(line.start, line.length), *data.bld); +} + +void Text::coords_to_geom_line(unsigned i, const Geometry &rgeom, CoordsToGeomData &data) const +{ + if(i==data.row) + { + const Line &line = lines[i]; + const GL::Font *font = style->get_font(); + + data.result = rgeom; + data.result.x += static_cast(font->get_string_width(text.substr(line.start, data.col))*font->get_default_size()); + } +} + } // namespace GLtk } // namespace Msp diff --git a/source/text.h b/source/text.h index 0e1afae..850e9fb 100644 --- a/source/text.h +++ b/source/text.h @@ -31,6 +31,9 @@ private: unsigned width; }; + struct RenderData; + struct CoordsToGeomData; + const Style *style; std::string text; std::vector lines; @@ -53,13 +56,19 @@ public: unsigned get_line_length(unsigned) const; void offset_to_coords(unsigned, unsigned &, unsigned &) const; unsigned coords_to_offset(unsigned, unsigned) const; - Geometry coords_to_geometry(unsigned, unsigned) const; + Geometry coords_to_geometry(const Part &, const Geometry &, unsigned, unsigned, unsigned) const; - void render(const Part &, const Geometry &) const; + void render(const Part &, const Geometry &, unsigned = 0) const; Text &operator=(const std::string &); private: void find_lines(); + + template + void process_lines(const Part &, const Geometry &, unsigned, T &) const; + + void render_line(unsigned, const Geometry &, RenderData &) const; + void coords_to_geom_line(unsigned, const Geometry &, CoordsToGeomData &) const; }; } // namespace GLtk -- 2.43.0