Playing with place: min, max, virtual functions, etc

Jun 24, 2013 at 9:07 PM
Edited Jun 24, 2013 at 9:14 PM
Hallo !
As you know I’m learning yet C++ to apply it in my work (I’m not a programmer).
Contrary to the big myth about the impossibility of learning C++ while programming a GUI, I have found Nana a wonderful way to learn it, having a lot of fun, and even explore the powerful possibilities of the new C++11.
That’s why I continued to study how some part of nana works. I wanted to add some features to “place”. The major of then is the possibility of defining a minimum weight for some widgets or fields in the layout. By now you need to decide a fixed, percent or adjustable field, but sometimes you prefer something in between – an adjustable field with a minimum or maximum weight. But I had difficult to use the implementation and I tried to make a simpler variant. I’m afraid I have not made it simpler, but I think it could be interesting for you to see some of my ideas. The major change is to transfer the complexity from the moment of use of the polymorphic fields to the moment of declaring it. I mean: not to use members to make a runtime check of the type, but let the C++ virtual function machinery do that, using inheritance. The inheritance is now much more complicate as I wanted, but the central functions with describe the functionality are a lot more simples and concentrated. I decided to unified divisions and fields under the IField interface. Also I decided to make the connection between the names used in the layout and the fields defined with the field (name)<<widget instruction only at the moment of collocate. This can be a litter more slow, but it is very much simpler: I keep the names in a set (they are unique) and the fields in a “global” multimap with the field names as key. Divisions are fields created on-the-fly by the scanner with automatic names. Each division hold a vector with the names inside it, and only just before collocate the children pointers are collected for each division. Collocate use a property (a virtual function) __of the field to calculate the positions.
Basically it support the same interfase, but with some differences:
The min and max, with you can add in the layout for each division, or you can use together with the << operator in the same way you change some parameters in streams (like cout<<boolalpha; etc.). You can add many names in one division, and the order is important. There are some diferences in the grid, but I have not tested it yet.
The code is in the branch place of myNana fork. All you need to test it is to temporally replace the place.hpp and cpp file from here in your current code.
The central functions with describe the functionality are:
pre_place(total_weight, fixed_adjust)
end_place(total_weight, fixed_adjust, adjust_min) 
with are consecutively called for each child field in the division, including child division, and with calculate some needed accumulative parameters to calculate the final field weight with
weight(total_weight, fixed_adjust, adjust_min)
The implementations of this function vary in dependence of the kind of field: fixed, percent or adjusted. This functions are used by the collocate function of the division (for others field collocate simple move and show the widget or do nothing) and are influenced by the kind of division.
```
template <class Base> struct IFixed:  IAdjust<Base>     
{ 
     unsigned weight_; 

    IFixed(unsigned weight)    : weight_(weight) {}
    IFixed(unsigned weight, unsigned min_,unsigned max_)
           : weight_(weight),IAdjust<Base>(min_,max_)     {}

  adj  pre_place (  unsigned t_w,  adj&   fixed = adj()   ) override    
  {   fixed.weigth   += weigth_adj(t_w) ;         return  fixed;    }

  adj end_place (  unsigned t_w,  const adj& fixed, adj& adj_min ) override    
 {   adj_min.weigth += weigth_adj(t_w) ;     return  adj_min; }   

unsigned weigth ( unsigned t_w, const adj& fixed,  const adj& adj_min   )  override              
      {   return weigth_adj(t_w);      }    

 virtual unsigned weigth_adj(unsigned t_w )
 {   
     if ( weight_  < min )    {return min; }
     if ( weight_  > max )    {return max; }

     return  weight_;          
 }
};

template <class Base> struct IPercent: IFixed<Base> 
{ 
   IPercent  ( double percent_)    :  IFixed<Base>(100*percent_) {}
IPercent ( double percent_,   unsigned min_,  unsigned max_ )
        : IFixed<Base>(100*percent_,min_,max_)   {}

unsigned weigth_adj(unsigned t_w )override
{   
    if ( t_w * weight_ /100.0  < min )  {return min; }
    if ( t_w * weight_ /100.0  > max )  {return max; }

    return  t_w * weight_ /100.0;          
  }
};     

template <class Base> struct IAdjustable  :  IAdjust<Base>                      
{ 
   IAdjustable(         ){}
   IAdjustable(unsigned min_,unsigned max_):IAdjust<Base>  (min_,max_){}

   adj   pre_place(unsigned t_w,   adj& fixed = adj() ) override    
   {  
       ++fixed.count_adj;     
       fixed.min += min;    
       return  fixed;        
   }
  adj  end_place ( unsigned t_w, const adj& fixed = adj(),  adj& adj_min = adj()) override    
 {   
    if ( t_w   <  fixed.weigth + fixed.min      )  
         { adj_min.weigth += min;    return adj_min; }

   if ( t_w < min * fixed.count_adj + fixed.weigth)
        { adj_min.weigth += min; return adj_min; }

 if ( t_w > max * fixed.count_adj + fixed.weigth)
      { adj_min.weigth += max; return adj_min; }

   adj_min.min += min; 
   ++adj_min.count_adj;   
   return  adj_min;        
}
unsigned weigth ( unsigned t_w,  const adj& fixed,  const adj& adj_min ) override
{   
     if ( t_w    <   fixed.weigth + fixed.min       )                  {return min; }
     if ( t_w < min * fixed.count_adj  + fixed.weigth )         {return min; }
     if ( t_w > max * fixed.count_adj  + fixed.weigth )        {return max; }
    if ( t_w < min * adj_min.count_adj + adj_min.weigth)  {return min; }
   if ( t_w >max * adj_min.count_adj + adj_min.weigth)   {return max; }

  return (t_w - adj_min.weigth)/adj_min.count_adj;        
 }
};
virtual void collocate( const rectangle& r) 
{           
   rectangle area (r);

  adj pre_adj, end_adj; auto t_w=weigth_s(area);
 for(auto child: children)                            
      child->pre_place(  t_w , pre_adj );  
 for(auto child: children)                            
      child->end_place(  t_w , pre_adj, end_adj );

  for(auto child : children)                          
 {
    rectangle child_area (area);
    weigth_s(child_area) = child->weigth(  t_w , pre_adj, end_adj )   ;
    weigth_c(area)      += weigth_s(child_area);
    weigth_s(area)      -= weigth_s(child_area);
    child->collocate(child_area);
  }
  for(auto & fsn: fastened_in_div)
 {  
     API::move_window(fsn, r);
     API::show_window(fsn,true);
  }
}

Coordinator
Jun 26, 2013 at 9:36 PM
Hi, qPCR4vir
First of all, I apperciate your work. I have studied your solution and had some points of view.

1, Returning a pointer may confuse people.
static IField*     fixed   (window wd, unsigned size         );
static IField*     percent (window wd, double   percent_ , minmax MinMax=minmax()    );
static IField*     room    (window wd, unsigned w, unsigned h);/// TODO: Add min and max
Because a C++ programmer always want to delete a pointer which is returned from a function. And this is why I defined some types for each one. But we can hide the IField within the implementation.


2, I totally agree with you that OO should be employed. But there is an issue.
place.div("<F grid [M, N]>");
place.field("F")<<place.room(btn, 1, 2);  //It's OK
But the division can be specified at run time. and when the div is changed to
place.div("<F>");
then the "room" is ignored and btn is laied out as place.field("F")<<btn;
This is a feature that place provides. The "room" is a suggestion for the field and it will be taken when field supports its behavior.