widgets.cpp
Go to the documentation of this file.
00001 /* This file is part of the Pangolin Project.
00002  * http://github.com/stevenlovegrove/Pangolin
00003  *
00004  * Copyright (c) 2011 Steven Lovegrove
00005  *
00006  * Permission is hereby granted, free of charge, to any person
00007  * obtaining a copy of this software and associated documentation
00008  * files (the "Software"), to deal in the Software without
00009  * restriction, including without limitation the rights to use,
00010  * copy, modify, merge, publish, distribute, sublicense, and/or sell
00011  * copies of the Software, and to permit persons to whom the
00012  * Software is furnished to do so, subject to the following
00013  * conditions:
00014  *
00015  * The above copyright notice and this permission notice shall be
00016  * included in all copies or substantial portions of the Software.
00017  *
00018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00019  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
00020  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00021  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
00022  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
00023  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00024  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00025  * OTHER DEALINGS IN THE SOFTWARE.
00026  */
00027 
00028 #include "widgets.h"
00029 
00030 #include <boost/thread/thread.hpp>
00031 #include <boost/thread/mutex.hpp>
00032 #include <iostream>
00033 #include <iomanip>
00034 #include "display_internal.h"
00035 
00036 using namespace std;
00037 
00038 namespace pangolin
00039 {
00040 
00041 extern __thread PangolinGl* context;
00042 
00043 const static int border = 1;
00044 const static int tab_w = 15;
00045 const static int tab_h = 20;
00046 const static int tab_p = 5;
00047 const static float colour_s1[4] = {0.2, 0.2, 0.2, 1.0};
00048 const static float colour_s2[4] = {0.6, 0.6, 0.6, 1.0};
00049 const static float colour_bg[4] = {0.9, 0.9, 0.9, 1.0};
00050 const static float colour_fg[4] = {1.0, 1.0 ,1.0, 1.0};
00051 const static float colour_tx[4] = {0.0, 0.0, 0.0, 1.0};
00052 const static float colour_hl[4] = {0.9, 0.9, 0.9, 1.0};
00053 const static float colour_dn[4] = {1.0, 0.7 ,0.7, 1.0};
00054 static void* font = GLUT_BITMAP_HELVETICA_12;
00055 static int text_height = 8; //glutBitmapHeight(font) * 0.7;
00056 static int cb_height = text_height * 1.6;
00057 
00058 boost::mutex display_mutex;
00059 
00060 static bool guiVarHasChanged = true;
00061 
00062 bool GuiVarHasChanged()
00063 {
00064     return pangolin::Pushed(guiVarHasChanged);
00065 }
00066 
00067 template<typename T>
00068 void GuiVarChanged( Var<T>& var)
00069 {
00070     guiVarHasChanged = true;
00071     var.var->meta_gui_changed = true;
00072 
00073     BOOST_FOREACH(GuiVarChangedCallback& gvc, gui_var_changed_callbacks)
00074     if( boost::starts_with(var.var->meta_full_name,gvc.filter) )
00075         gvc.fn(gvc.data,var.var->meta_full_name,*var.var);
00076 }
00077 
00078 void glRect(Viewport v)
00079 {
00080     glRecti(v.l,v.b,v.r(),v.t());
00081 }
00082 
00083 void glRect(Viewport v, int inset)
00084 {
00085     glRecti(v.l+inset,v.b+inset,v.r()-inset,v.t()-inset);
00086 }
00087 
00088 void DrawShadowRect(Viewport& v)
00089 {
00090     glColor4fv(colour_s2);
00091     glBegin(GL_LINE_STRIP);
00092     glVertex2i(v.l,v.b);
00093     glVertex2i(v.l,v.t());
00094     glVertex2i(v.r(),v.t());
00095     glVertex2i(v.r(),v.b);
00096     glVertex2i(v.l,v.b);
00097     glEnd();
00098 }
00099 
00100 void DrawShadowRect(Viewport& v, bool pushed)
00101 {
00102     glColor4fv(pushed ? colour_s1 : colour_s2);
00103     glBegin(GL_LINE_STRIP);
00104     glVertex2i(v.l,v.b);
00105     glVertex2i(v.l,v.t());
00106     glVertex2i(v.r(),v.t());
00107     glEnd();
00108 
00109     glColor3fv(pushed ? colour_s2 : colour_s1);
00110     glBegin(GL_LINE_STRIP);
00111     glVertex2i(v.r(),v.t());
00112     glVertex2i(v.r(),v.b);
00113     glVertex2i(v.l,v.b);
00114     glEnd();
00115 }
00116 
00117 Panel::Panel()
00118     : context_views(context->named_managed_views)
00119 {
00120     handler = &StaticHandlerScroll;
00121     layout = LayoutVertical;
00122 }
00123 
00124 Panel::Panel(const std::string& auto_register_var_prefix)
00125     : context_views(context->named_managed_views)
00126 {
00127     handler = &StaticHandlerScroll;
00128     layout = LayoutVertical;
00129     RegisterNewVarCallback(&Panel::AddVariable,(void*)this,auto_register_var_prefix);
00130 
00131     // TODO: Work out how this might work
00132     //    ProcessHistoricCallbacks(&Panel::AddVariable,(void*)this,auto_register_var_prefix);
00133 }
00134 
00135 void Panel::AddVariable(void* data, const std::string& name, _Var& var, const char* reg_type_name, bool brand_new )
00136 {
00137     Panel* thisptr = (Panel*)data;
00138 
00139     const string& title = var.meta_friendly;
00140 
00141     display_mutex.lock();
00142 
00143     boost::ptr_unordered_map<const std::string,View>::iterator pnl =
00144         thisptr->context_views.find(name);
00145 
00146     // Only add if a widget by the same name doesn't
00147     // already exist
00148     if( pnl == thisptr->context_views.end() )
00149     {
00150         if( reg_type_name == typeid(bool).name() )
00151         {
00152             View* nv = var.meta_flags ? (View*)new Checkbox(title,var) : (View*)new Button(title,var);
00153             //thisptr->context_views[name] = nv;
00154             thisptr->context_views.insert(name,nv);
00155             thisptr->views.push_back(nv);
00156             thisptr->ResizeChildren();
00157         }
00158         else if( reg_type_name == typeid(double).name() || reg_type_name == typeid(float).name() || reg_type_name == typeid(int).name() || reg_type_name == typeid(unsigned int).name() )
00159         {
00160             View* nv = new Slider(title,var);
00161             //thisptr->context_views[name] = nv;
00162             thisptr->context_views.insert(name,nv);
00163             thisptr->views.push_back( nv );
00164             thisptr->ResizeChildren();
00165         }
00166         else
00167         {
00168             View* nv = new TextInput(title,var);
00169             //thisptr->context_views[name] = nv;
00170             thisptr->context_views.insert(name,nv);
00171             thisptr->views.push_back( nv );
00172             thisptr->ResizeChildren();
00173         }
00174     }
00175 
00176     display_mutex.unlock();
00177 }
00178 
00179 void Panel::Render()
00180 {
00181     glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_SCISSOR_BIT | GL_VIEWPORT_BIT);
00182 
00183     OpenGlRenderState::ApplyWindowCoords();
00184     glDisable(GL_DEPTH_TEST);
00185     glDisable(GL_SCISSOR_TEST);
00186     glDisable(GL_LINE_SMOOTH);
00187     glLineWidth(1.0);
00188 
00189     glColor4fv(colour_s2);
00190     glRect(v);
00191     glColor4fv(colour_bg);
00192     glRect(v,1);
00193 
00194     RenderChildren();
00195 
00196     glPopAttrib();
00197 }
00198 
00199 void Panel::ResizeChildren()
00200 {
00201     View::ResizeChildren();
00202 }
00203 
00204 
00205 View& CreatePanel(const std::string& name)
00206 {
00207     Panel * p = new Panel(name);
00208     bool inserted = context->named_managed_views.insert(name, p).second;
00209     if(!inserted) throw exception();
00210     context->base.views.push_back(p);
00211     return *p;
00212 }
00213 
00214 Button::Button(string title, _Var& tv)
00215     : Widget<bool>(title,tv), down(false)
00216 {
00217     top = 1.0;
00218     bottom = Attach::Pix(-20);
00219     left = 0.0;
00220     right = 1.0;
00221     hlock = LockLeft;
00222     vlock = LockBottom;
00223     text_width = glutBitmapLength(font,(unsigned char*)title.c_str());
00224 }
00225 
00226 void Button::Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state)
00227 {
00228     if(button == MouseButtonLeft )
00229     {
00230         down = pressed;
00231         if( !pressed )
00232         {
00233             a->Set(!a->Get());
00234             GuiVarChanged(*this);
00235         }
00236     }
00237 }
00238 
00239 void Button::Render()
00240 {
00241     glColor4fv(colour_fg );
00242     glRect(v);
00243     glColor4fv(colour_tx);
00244     glRasterPos2f(raster[0],raster[1]-down);
00245     glutBitmapString(font,(unsigned char*)title.c_str());
00246     DrawShadowRect(v, down);
00247 }
00248 
00249 void Button::ResizeChildren()
00250 {
00251     raster[0] = v.l + (v.w-text_width)/2.0;
00252     raster[1] = v.b + (v.h-text_height)/2.0;
00253     vinside = v.Inset(border);
00254 }
00255 
00256 Checkbox::Checkbox(std::string title, _Var& tv)
00257     : Widget<bool>(title,tv)
00258 {
00259     top = 1.0;
00260     bottom = Attach::Pix(-20);
00261     left = 0.0;
00262     right = 1.0;
00263     hlock = LockLeft;
00264     vlock = LockBottom;
00265     handler = this;
00266 }
00267 
00268 void Checkbox::Mouse(View&, MouseButton button, int x, int y, bool pressed, int mouse_state)
00269 {
00270     if( button == MouseButtonLeft && pressed )
00271     {
00272         a->Set(!a->Get());
00273         GuiVarChanged(*this);
00274     }
00275 }
00276 
00277 void Checkbox::ResizeChildren()
00278 {
00279     raster[0] = v.l + cb_height + 4;
00280     raster[1] = v.b + (v.h-text_height)/2.0;
00281     const int h = v.h;
00282     const int t = (h-cb_height) / 2.0;
00283     vcb = Viewport(v.l,v.b+t,cb_height,cb_height);
00284 }
00285 
00286 void Checkbox::Render()
00287 {
00288     const bool val = a->Get();
00289 
00290     if( val )
00291     {
00292         glColor4fv(colour_dn);
00293         glRect(vcb);
00294     }
00295     glColor4fv(colour_tx);
00296     glRasterPos2fv( raster );
00297     glutBitmapString(font,(unsigned char*)title.c_str());
00298     DrawShadowRect(vcb, val);
00299 }
00300 
00301 
00302 Slider::Slider(std::string title, _Var& tv)
00303     : Widget<double>(title+":", tv), lock_bounds(true)
00304 {
00305     top = 1.0;
00306     bottom = Attach::Pix(-20);
00307     left = 0.0;
00308     right = 1.0;
00309     hlock = LockLeft;
00310     vlock = LockBottom;
00311     handler = this;
00312     logscale = (int)tv.logscale;
00313 }
00314 
00315 void Slider::Keyboard(View&, unsigned char key, int x, int y, bool pressed)
00316 {
00317 
00318     if( pressed && var->meta_range[0] < var->meta_range[1] )
00319     {
00320         double val = !logscale ? a->Get() : log(a->Get());
00321 
00322         if(key=='-')
00323         {
00324             if (logscale)
00325                 a->Set( exp(max(var->meta_range[0],min(var->meta_range[1],val - var->meta_increment) ) ) );
00326             else
00327                 a->Set( max(var->meta_range[0],min(var->meta_range[1],val - var->meta_increment) ) );
00328         }
00329         else if(key == '=')
00330         {
00331             if (logscale)
00332                 a->Set( exp(max(var->meta_range[0],min(var->meta_range[1],val + var->meta_increment) ) ) );
00333             else
00334                 a->Set( max(var->meta_range[0],min(var->meta_range[1],val + var->meta_increment) ) );
00335         }
00336         else if(key == 'r')
00337         {
00338             Reset();
00339         }
00340         else
00341         {
00342             return;
00343         }
00344         GuiVarChanged(*this);
00345     }
00346 }
00347 
00348 void Slider::Mouse(View& view, MouseButton button, int x, int y, bool pressed, int mouse_state)
00349 {
00350     if(pressed)
00351     {
00352         // Wheel
00353         if( button == MouseWheelUp || button == MouseWheelDown )
00354         {
00355             // Change scale around current value
00356             const double frac = max(0.0,min(1.0,(double)(x - v.l)/(double)v.w));
00357             double val = frac * (var->meta_range[1] - var->meta_range[0]) + var->meta_range[0];
00358 
00359             if (logscale)
00360             {
00361                 if (val<=0)
00362                     val = std::numeric_limits<double>::min();
00363                 else
00364                     val = log(val);
00365             }
00366 
00367             const double scale = (button == MouseWheelUp ? 1.2 : 1.0 / 1.2 );
00368             var->meta_range[1] = val + (var->meta_range[1] - val)*scale;
00369             var->meta_range[0] = val - (val - var->meta_range[0])*scale;
00370         }
00371         else
00372         {
00373             lock_bounds = (button == MouseButtonLeft);
00374             MouseMotion(view,x,y,mouse_state);
00375         }
00376     }
00377     else
00378     {
00379         if(!lock_bounds)
00380         {
00381             double val = !logscale ? a->Get() : log(a->Get());
00382 
00383             var->meta_range[0] = min(var->meta_range[0], val);
00384             var->meta_range[1] = max(var->meta_range[1], val);
00385         }
00386     }
00387 }
00388 
00389 void Slider::MouseMotion(View&, int x, int y, int mouse_state)
00390 {
00391     if( var->meta_range[0] != var->meta_range[1] )
00392     {
00393         const double range = (var->meta_range[1] - var->meta_range[0]);
00394         const double frac = (double)(x - v.l)/(double)v.w;
00395         double val;
00396 
00397         if( lock_bounds )
00398         {
00399             const double bfrac = max(0.0,min(1.0,frac));
00400             val = bfrac * range + var->meta_range[0] ;
00401         }
00402         else
00403         {
00404             val = frac * range + var->meta_range[0];
00405         }
00406 
00407         if (logscale) val = exp(val);
00408 
00409         a->Set(val);
00410         GuiVarChanged(*this);
00411     }
00412 }
00413 
00414 
00415 void Slider::ResizeChildren()
00416 {
00417     raster[0] = v.l+2;
00418     raster[1] = v.b + (v.h-text_height)/2.0;
00419 }
00420 
00421 void Slider::Render()
00422 {
00423     const double val = a->Get();
00424 
00425     if( var->meta_range[0] != var->meta_range[1] )
00426     {
00427         double rval = val;
00428         if (logscale)
00429         {
00430             rval = log(val);
00431         }
00432         glColor4fv(colour_fg);
00433         glRect(v);
00434         glColor4fv(colour_dn);
00435         const double norm_val = max(0.0,min(1.0,(rval - var->meta_range[0]) / (var->meta_range[1] - var->meta_range[0])));
00436         glRect(Viewport(v.l,v.b,v.w*norm_val,v.h));
00437         DrawShadowRect(v);
00438     }
00439 
00440     glColor4fv(colour_tx);
00441     glRasterPos2fv( raster );
00442     glutBitmapString(font,(unsigned char*)title.c_str());
00443 
00444     std::ostringstream oss;
00445     oss << setprecision(4) << val;
00446     string str = oss.str();
00447     const int l = glutBitmapLength(font,(unsigned char*)str.c_str()) + 2;
00448     glRasterPos2f( v.l + v.w - l, raster[1] );
00449     glutBitmapString(font,(unsigned char*)str.c_str());
00450 }
00451 
00452 
00453 TextInput::TextInput(std::string title, _Var& tv)
00454     : Widget<std::string>(title+":", tv), do_edit(false)
00455 {
00456     top = 1.0;
00457     bottom = Attach::Pix(-20);
00458     left = 0.0;
00459     right = 1.0;
00460     hlock = LockLeft;
00461     vlock = LockBottom;
00462     handler = this;
00463     sel[0] = -1;
00464     sel[1] = -1;
00465 }
00466 
00467 void TextInput::Keyboard(View&, unsigned char key, int x, int y, bool pressed)
00468 {
00469     if(pressed)
00470     {
00471         const bool selection = sel[1] > sel[0] && sel[0] >= 0;
00472 
00473         if(key == 13)
00474         {
00475             a->Set(edit);
00476             GuiVarChanged(*this);
00477 
00478             do_edit = false;
00479             sel[0] = sel[1] = -1;
00480         }
00481         else if(key == 8)
00482         {
00483             // backspace
00484             if(selection)
00485             {
00486                 edit = edit.substr(0,sel[0]) + edit.substr(sel[1],edit.length()-sel[1]);
00487                 sel[1] = sel[0];
00488             }
00489             else
00490             {
00491                 if(sel[0] >0)
00492                 {
00493                     edit = edit.substr(0,sel[0]-1) + edit.substr(sel[0],edit.length()-sel[0]);
00494                     sel[0]--;
00495                     sel[1]--;
00496                 }
00497             }
00498         }
00499         else if(key == 127)
00500         {
00501             // delete
00502             if(selection)
00503             {
00504                 edit = edit.substr(0,sel[0]) + edit.substr(sel[1],edit.length()-sel[1]);
00505                 sel[1] = sel[0];
00506             }
00507             else
00508             {
00509                 if(sel[0] < (int)edit.length())
00510                 {
00511                     edit = edit.substr(0,sel[0]) + edit.substr(sel[0]+1,edit.length()-sel[0]+1);
00512                 }
00513             }
00514         }
00515         else if(key == 230)
00516         {
00517             // right
00518             sel[0] = min((int)edit.length(),sel[0]+1);
00519             sel[1] = sel[0];
00520         }
00521         else if(key == 228)
00522         {
00523             // left
00524             sel[0] = max(0,sel[0]-1);
00525             sel[1] = sel[0];
00526         }
00527         else if(key == 234)
00528         {
00529             // home
00530             sel[0] = sel[1] = 0;
00531         }
00532         else if(key == 235)
00533         {
00534             // end
00535             sel[0] = sel[1] = edit.length();
00536         }
00537         else
00538         {
00539             //            cout << (int)key << endl;
00540             edit = edit.substr(0,sel[0]).append(1,key) + edit.substr(sel[1],edit.length()-sel[1]);
00541             sel[1] = sel[0];
00542             sel[0]++;
00543             sel[1]++;
00544         }
00545     }
00546 }
00547 
00548 void TextInput::Mouse(View& view, MouseButton button, int x, int y, bool pressed, int mouse_state)
00549 {
00550     if(button != MouseWheelUp && button != MouseWheelDown )
00551     {
00552 
00553         if(do_edit)
00554         {
00555             const int sl = glutBitmapLength(font,(unsigned char*)edit.c_str()) + 2;
00556             const int rl = v.l + v.w - sl;
00557             int ep = edit.length();
00558 
00559             if( x < rl )
00560             {
00561                 ep = 0;
00562             }
00563             else
00564             {
00565                 for( unsigned i=0; i<edit.length(); ++i )
00566                 {
00567                     const int tl = rl + glutBitmapLength(font,(unsigned char*)edit.substr(0,i).c_str());
00568                     if(x < tl+2)
00569                     {
00570                         ep = i;
00571                         break;
00572                     }
00573                 }
00574             }
00575             if(pressed)
00576             {
00577                 sel[0] = sel[1] = ep;
00578             }
00579             else
00580             {
00581                 sel[1] = ep;
00582             }
00583 
00584             if(sel[0] > sel[1])
00585                 std::swap(sel[0],sel[1]);
00586         }
00587         else
00588         {
00589             do_edit = !pressed;
00590             sel[0] = 0;
00591             sel[1] = edit.length();
00592         }
00593     }
00594 }
00595 
00596 void TextInput::MouseMotion(View&, int x, int y, int mouse_state)
00597 {
00598     if(do_edit)
00599     {
00600         const int sl = glutBitmapLength(font,(unsigned char*)edit.c_str()) + 2;
00601         const int rl = v.l + v.w - sl;
00602         int ep = edit.length();
00603 
00604         if( x < rl )
00605         {
00606             ep = 0;
00607         }
00608         else
00609         {
00610             for( unsigned i=0; i<edit.length(); ++i )
00611             {
00612                 const int tl = rl + glutBitmapLength(font,(unsigned char*)edit.substr(0,i).c_str());
00613                 if(x < tl+2)
00614                 {
00615                     ep = i;
00616                     break;
00617                 }
00618             }
00619         }
00620 
00621         sel[1] = ep;
00622     }
00623 }
00624 
00625 
00626 void TextInput::ResizeChildren()
00627 {
00628     raster[0] = v.l+2;
00629     raster[1] = v.b + (v.h-text_height)/2.0;
00630 }
00631 
00632 void TextInput::Render()
00633 {
00634     if(!do_edit) edit = a->Get();
00635 
00636     glColor4fv(colour_fg);
00637     glRect(v);
00638 
00639     const int sl = glutBitmapLength(font,(unsigned char*)edit.c_str()) + 2;
00640     const int rl = v.l + v.w - sl;
00641 
00642     if( do_edit && sel[0] >= 0)
00643     {
00644         const int tl = rl + glutBitmapLength(font,(unsigned char*)edit.substr(0,sel[0]).c_str());
00645         const int tr = rl + glutBitmapLength(font,(unsigned char*)edit.substr(0,sel[1]).c_str());
00646         glColor4fv(colour_dn);
00647         glRect(Viewport(tl,v.b,tr-tl,v.h));
00648     }
00649 
00650     glColor4fv(colour_tx);
00651     glRasterPos2fv( raster );
00652     glutBitmapString(font,(unsigned char*)title.c_str());
00653 
00654     glRasterPos2f( rl, raster[1] );
00655     glutBitmapString(font,(unsigned char*)edit.c_str());
00656     DrawShadowRect(v);
00657 }
00658 
00659 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines


pangolin_wrapper
Author(s): Todor Stoyanov
autogenerated on Wed Feb 13 2013 14:03:25