]> git.tdb.fi Git - libs/gltk.git/blobdiff - source/entry.cpp
Add a VSlider to multiline Entries
[libs/gltk.git] / source / entry.cpp
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);
+       }
 }