]> git.tdb.fi Git - libs/gltk.git/commitdiff
Add a VSlider to multiline Entries
authorMikko Rasa <tdb@tdb.fi>
Wed, 6 Oct 2010 12:28:47 +0000 (12:28 +0000)
committerMikko Rasa <tdb@tdb.fi>
Wed, 6 Oct 2010 12:28:47 +0000 (12:28 +0000)
Make Text only render as many lines as fit in the widget

source/entry.cpp
source/entry.h
source/text.cpp
source/text.h

index 91bd9c0ce82072219bdd46efa48e2d40e39392dc..654ac6c673f17025917f72ad1af4bf0cdcde0631 100644 (file)
@@ -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_pos<text.size())
+               {
                        ++edit_pos;
+                       check_view_range();
+               }
        }
        else if(key==Input::KEY_DOWN && multiline)
        {
                unsigned row, col;
                text.offset_to_coords(edit_pos, row, col);
                edit_pos = text.coords_to_offset(row+1, col);
+               check_view_range();
        }
        else if(key==Input::KEY_UP && multiline)
        {
                unsigned row, col;
                text.offset_to_coords(edit_pos, row, col);
                if(row>0)
+               {
                        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 || 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<Part>::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<Part>::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<unsigned>(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<unsigned>(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);
+       }
 }
 
 
index 0ff5466a613e11bcd545002b4409727b253ef9ef..a8bbcd5d49f865c77ac0e276271684b62bb62590 100644 (file)
@@ -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<void> 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
index 5fc546c21dfe30a0c2eb9eb30662703490bf0c7e..1b465120a31d5b5b50ff699969c646aaa5f625a8 100644 (file)
@@ -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<unsigned>((font->get_ascent()-font->get_descent())*font_size);
-       unsigned line_spacing = static_cast<unsigned>(font_size*6/5);
-       unsigned height = line_height+(lines.size()-1)*line_spacing;
-       int y_offset = static_cast<int>(-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<unsigned>(font->get_string_width(text.substr(line.start, col))*font_size);
-       geom.y = (lines.size()-1-row)*line_spacing+y_offset;
+       process_lines<CoordsToGeomData, &Text::coords_to_geom_line>(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<unsigned>((font->get_ascent()-font->get_descent())*font_size);
-       unsigned line_spacing = static_cast<unsigned>(font_size*6/5);
-       unsigned height = line_height+(lines.size()-1)*line_spacing;
-       int y_offset = static_cast<int>(-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; i<lines.size(); ++i)
-       {
-               const Line &line = lines[i];
-
-               Geometry rgeom;
-               rgeom.w = line.width;
-               rgeom.h = height;
-               rgeom.y = (lines.size()-1-i)*line_spacing+y_offset;
-               part.get_alignment().apply(rgeom, geom, part.get_margin());
-
-               GL::push_matrix();
-               GL::translate(rgeom.x, rgeom.y, 0);
-               GL::scale_uniform(font_size);
 
-               font->draw_string(text.substr(line.start, line.length), imm);
+       RenderData data;
+       data.bld = &imm;
 
-               GL::pop_matrix();
-       }
+       process_lines<RenderData, &Text::render_line>(part, parent, first_row, data);
 }
 
 Text &Text::operator=(const string &t)
@@ -212,5 +198,57 @@ void Text::find_lines()
        }
 }
 
+template<typename T, void (Text::*func)(unsigned, const Geometry &, T &) const>
+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<unsigned>((font->get_ascent()-font->get_descent())*font_size);
+       unsigned line_spacing = static_cast<unsigned>(font_size*6/5);
+       unsigned height = line_height+(lines.size()-1)*line_spacing;
+       int y_offset = static_cast<int>(-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<n_lines; ++i)
+       {
+               const Line &line = lines[first_row+i];
+
+               Geometry rgeom;
+               rgeom.w = line.width;
+               rgeom.h = height;
+               rgeom.y = (n_lines-1-i)*line_spacing+y_offset;
+               part.get_alignment().apply(rgeom, parent, part.get_margin());
+
+               (this->*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<unsigned>(font->get_string_width(text.substr(line.start, data.col))*font->get_default_size());
+       }
+}
+
 } // namespace GLtk
 } // namespace Msp
index 0e1afaebd198b3230736d38bf1c198b7cc4dc25c..850e9fb6f8c65d19222fe171eb6c463a615f303d 100644 (file)
@@ -31,6 +31,9 @@ private:
                unsigned width;
        };
 
+       struct RenderData;
+       struct CoordsToGeomData;
+
        const Style *style;
        std::string text;
        std::vector<Line> 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<typename T, void (Text::*)(unsigned, const Geometry &, T &) const>
+       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