]> git.tdb.fi Git - libs/gltk.git/blobdiff - source/entry.cpp
Store the Resources reference only in Root widget
[libs/gltk.git] / source / entry.cpp
index 496a79d0da017421361126742ec9ad76ad6fcae9..2c9cb357fa55d38215c64358b4f1ae22fe972932 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id$
 
 This file is part of libmspgltk
-Copyright © 2007  Mikko Rasa, Mikkosoft Productions
+Copyright © 2007-2010  Mikko Rasa, Mikkosoft Productions
 Distributed under the LGPL
 */
 
@@ -13,25 +13,49 @@ Distributed under the LGPL
 #include "graphic.h"
 #include "part.h"
 #include "style.h"
+#include "vslider.h"
 
 using namespace std;
 
 namespace Msp {
 namespace GLtk {
 
-Entry::Entry(const Resources &r, const string &t):
-       Widget(r),
-       text(style),
-       edit_pos(0)
+Entry::Entry(const string &t):
+       text(),
+       multiline(false),
+       edit_pos(0),
+       first_row(0),
+       visible_rows(1),
+       text_part(0),
+       slider(0)
 {
-       update_style();
        set_text(t);
 }
 
 void Entry::set_text(const string &t)
 {
-       text=t;
-       edit_pos=text.size();
+       text = t;
+       edit_pos = text.size();
+
+       if(multiline)
+               check_view_range();
+}
+
+void Entry::set_multiline(bool m)
+{
+       multiline = m;
+       if(multiline)
+       {
+               if(!slider)
+               {
+                       slider = new VSlider;
+                       add(*slider);
+                       slider->set_step(1);
+                       slider->signal_value_changed.connect(sigc::mem_fun(this, &Entry::slider_value_changed));
+                       reposition_slider();
+               }
+               check_view_range();
+       }
 }
 
 void Entry::key_press(unsigned key, unsigned, wchar_t ch)
@@ -39,20 +63,56 @@ 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)
-               signal_enter.emit();
+       {
+               if(multiline)
+               {
+                       text.insert(edit_pos++, "\n");
+                       check_view_range();
+               }
+               else
+                       signal_enter.emit();
+       }
        else if(ch>=' ')
        {
                text.insert(edit_pos, Codecs::encode<Codecs::Utf8>(Codecs::ustring(1, ch)));
@@ -63,30 +123,100 @@ 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;
 
-               const GL::Font *const font=style->get_font();
-               const float font_size=font->get_default_size();
+               unsigned row, col;
+               text.offset_to_coords(edit_pos, row, col);
+
+               if(row<first_row || row>=first_row+visible_rows)
+                       return;
 
-               Geometry rgeom=part.get_geometry();
-               rgeom.x=static_cast<unsigned>(font->get_string_width(text.get().substr(0, edit_pos))*font_size);
-               rgeom.w=static_cast<unsigned>(font->get_string_width(text.get())*font_size);
-               part.get_alignment().apply(rgeom, geom, part.get_margin());
+               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, rgeom.h);
+               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();
+
+       if(multiline)
+               check_view_range();
 }
 
 void Entry::on_style_change()
 {
-       text.update_style();
+       text.set_style(style);
+
+       if(!style)
+       {
+               text_part = 0;
+               return;
+       }
+
+       text_part = style->get_part("text");
+
+       reposition_slider();
+
+       if(multiline)
+               check_view_range();
+}
+
+void Entry::reposition_slider()
+{
+       if(!style || !slider)
+               return;
+
+       if(const Part *slider_part = style->get_part("slider"))
+       {
+               Geometry sgeom = slider_part->get_geometry();
+               slider_part->get_alignment().apply(sgeom, geom, slider_part->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 = max((geom.h-margin.top-margin.bottom)/line_spacing, 1U);
+
+       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);
+       }
 }