Cell format in listbox

Nov 24, 2014 at 5:37 AM
Edited Nov 24, 2014 at 3:55 PM
Hi !!
Well... finally I finded a way to show what I mean:
(but I still need a way to set in a column with sequences a font with fixed width, like curier new. (non porportional?) but it is not trivial.)
A major concern was not to broke existing code... or minimaly. The only think to change is the return type of decode(), but you can still return a string that will be correctly converted.
Image

The changes in listbox.hpp and listbox.cpp are in a pull recuest.
Here is part of my user code:
class ListSeqMaker : public List::resolver_interface <pSec>
 {
    List::cell decode(size_t col, const pSec &sec) const override
    {
       static const long    blen{ 50 }, slen{ 1000 };
       nana::char_t val[blen];

       switch (col)
       {
            case 0: return nana::string(nana::charset(sec->Name()));
            case 1: swprintf(val,blen,   STR("%*d"), 6,  sec->Len()  );
                       return val;
            case 2: { Temperature t=KtoC( sec->NonDegSet() ? 
                                          sec->NonDegSet()->_Local._Tm.Ave()
                                        : sec->_Tm.Ave() );
                    swprintf(val,blen, STR("% *.*f °C"), 6, 1,   t );

                    Temperature min=57.0, max=63.0;
                    double fade_rate=  t<min? 0.0 : t>max? 1.0 : (t-min)/(max-min);
                    nana::color_t   tc = 0xFFFFFFFF, 
                                     bc = nana::color::mix(nana::color::blue, 
                                                   nana::color::red, fade_rate); 
                    return {val, bc , tc};
                    }
            case 3: swprintf(val,blen,   STR("%*d") , 5, sec->Degeneracy());
                      return val;  
            case 4: return nana::string(nana::charset( sec->Description()));
            case 5: return nana::string(nana::charset(  (char *)(  
                  sec->Sequence().substr (1, std::min( sec->Len(), slen)).c_str() ))) ;

            default:
                return nana::string{};
            }
        }
...
Nov 24, 2014 at 4:01 PM
Edited Nov 24, 2014 at 4:06 PM
Image
updated
Coordinator
Nov 24, 2014 at 8:37 PM
Hi,
This is a great enhancement, and it inspired me, I will provide a more better and convenient class for the "resolver". Thank you very much.
Nov 28, 2014 at 10:42 AM
Edited Nov 28, 2014 at 4:21 PM
Hi !
[I updated the pull request]. I wanted to illustrate the second part, about colors.
I added this to the beginning of my main(): This are just random colors to see how it works.
nana::color::current_schema[nana::color::schema::list_header_border]=nana::color::Red;
nana::color::current_schema[nana::color::schema::list_header_bg]=nana::color::Yellow;
nana::color::current_schema[nana::color::schema::list_header_highlighted_bg]=nana::color::Rose;
nana::color::current_schema[nana::color::schema::list_header_pressed_bg]=nana::color::AliceBlue;  
nana::color::current_schema[nana::color::schema::list_header_grabed_bg]=nana::color::Ash_Gray;
nana::color::current_schema[nana::color::schema::list_header_floated_bg]=nana::color::Aztech_Purple; 
Image

The point is that you have just added an internationalization tool. Place take a "simple" string and make the layout. With a way to set the colors and fonts we will have the basis for a future good tool for customization. I don't think it is a big priority now for nana, but it could be for nana V2 or 3. I think this changes could make a littler easy to do that big change in the future.
Very important: this mechanism don't break anything and dont force to use it. It is truly optional.
It need to be thread safe, protecting current_schema in the moment of modification, but only. I dont know yet how, but it have to be "for free" if you don't use it. We could use some function that can be called only before any other use of nana... Or just lets that very clear in documentation and lets the programer take the responsibility.
Dec 5, 2014 at 9:45 PM
I like the new i-o-resolvers but I have a some problems.
I have this:
    typedef int index;  

    void Repopulate()
    {
        for (auto &i : _list.at(0))
            i.resolve_from(i.value<index>());
    }
    class ListTableMaker : public List::resolver_interface <index>
    {
        int     &n_dec,   &n_len;
        value   **val  ;

       List::cell decode(size_t col, const index &row) const override
        {
            if (col)        
                if ((*val)->return_bg() )
                    return {print ( (**val)  (row,index(col-1) )),
                            (*val)->bg_color(row,index(col-1) ),
                            nana::color::White};
                else return print ((**val)  (row,index(col-1) )); 

           return nana::string(nana::charset(  (**val) .table->TitRow(row)  ));
        }
        void encode(index&, std::size_t col, const nana::string& txt) const override
        {
           //if (col)
           //    (*table)(row,col-1)._Tm= CtoK(wstr_f(txt   ));
           //table->TitRow(row)=nana::charset(txt );
        }
  
        nana::string print(float n) const
        {
            static const int    blen{ 50 } ;
            static nana::char_t val_[blen]  ;

            swprintf(val_, blen, STR("% *.*f"), n_len, n_dec, n );
            return val_;
        }
     public:
        ListTableMaker( value *&val_, int &dec , int &len) : val{&val_},  n_len{len}, n_dec{dec}{}
        //void SetValType(value *&val_){ val = &val_;};
        //void SetFormat(int dec=1 , int len=6){ n_len=len; n_dec=dec;}
    };
Coordinator
Dec 5, 2014 at 10:12 PM
Do you mean it is hard to refer external data by using i/oresolver?
Dec 6, 2014 at 12:15 PM
Well, now objects of type i/o resolver are created internally and the users can not control its creation.
Here we see two problems: no acces to any state of the resolver, and predefined types can not be reused (or it can?), like the int I used for index.
I think the problem is not easy and any solution have to have some different forms of solution.
Here I'm introducing a new type: class Inex{inex i; listbox &lst;} an use it as item value to solve this two problems: the operator<< and >>(const Index& ix) have now all the information.
I think it is very good to offer simple solution for simple cases, like i/o resolver offer. This will be good for the majority of cases. But then we need to educate an offer user correct way to solve more complex cases. I will show you my solution and hope you can tell me what can be better.
Thank.
Coordinator
Dec 6, 2014 at 2:24 PM
I am looking forward to your whole solution.

Thanks
Jinhao
Dec 6, 2014 at 8:52 PM
Edited Dec 6, 2014 at 9:02 PM
using List = nana::listbox;

class TableRes  : public nana::form, public EditableForm
{   
    using Table = CTable<TmGPos> ;
    std::shared_ptr<Table> _table;
    List                   _list { *this };
    value                  *val { &_Tm} ;

    void SetValType(value &val_)
    { 
        _list.auto_draw(false);
        bool freeze{true};
        freeze=_list.freeze_sort(freeze);

        val = &val_;
        Repopulate();
        
        _list.auto_draw(true);
        _list.freeze_sort(freeze);
    }
    void Repopulate()
    {
        for (auto &i : _list.at(0))
            i.resolve_from(i.value<Index>());
    }

   //  ....
 public:
    friend struct Index;

    struct Index
    {
        TableRes* table;
        index       row;

      friend List::oresolver& operator<<(List::oresolver& ores, const TableRes::Index& i)
        {
            auto &t = *i.table->_table.get();
            auto &v = *i.table->val;
            ores<< t.TitRow(i.row)   ;
                
            if  (v.return_bg() )
                for (int col=0; col< t.totalCol() ; ++col)
                    ores<< List::cell{ v.str     (i.row, col),
                                       v.bg_color(i.row, col),
                                       nana::color::White};
            else 
                for (int col=0; col< t.totalCol() ; ++col)
                    ores<< v.str(i.row, col)  ;

            return ores;
        }
    };
   friend List::oresolver& operator<<(List::oresolver& ores, const TableRes::Index& i);
I call Set Value Type and it dynamically change the whole table including format and an internal state using a pointer back to the owner.
This because I have many tables of the same type but with different data and different state. One minor problem is that the value in the first column is defined with append, but overwrite with <<(). If I do not rewrite the first column in <<(Index &) I have to introduce some new type for this column only if I want to make some formatting.
Well, I will try again.
Image
Coordinator
Dec 6, 2014 at 11:15 PM
I got it, it was because you used a built-in int type directly as a data inserted to the listbox. Think about the std::cout<<5; the std::cout just outputs 5, not the 5th element in a certain container.

I had thought about adding a method that does not modify the same value in a specified column to the resolver. It looks like this
iresolver& operator>>(std::nullptr_t);
oresolver& operator<<(std::nullptr_t);

//For example

ores<<"DQ211647"<<52.3<<50.1; //first time at append()

//then, repopulate, a flag is required for determining whether or not rewrite the the specified columns.
if(not_rewrite)
    ores<<nullptr<<52.3<<nullptr; //values in the 1st and 3rd columns will not be changed
else
    ores<<"DQ211647"<<52.3<<50.1;
Other issue.
auto & i = list.append(t).value(t); //because of auto & i and return type of value() is a reference, so it is dangerous.
So, Add a new method
template<typename T> item_proxy append(const T& t);

template<typename T, typename ...Args> item_proxy emplace_back(Arg&&... args) //like append(const T& t, bool track)
{
    T t{std::forward<Args>(args)...};
    auto i = append(t);
    i.value(std::move(t)); // add a move-semantics value(), good efficiency if T is a BIG object.
    return i;
}