Posted by: nanaproject | January 31, 2012

Idioms and Insights for a Good Design

Assuming the design of a framework that generates some data and it requires a module for representing these data in GUI. In general, we just introduce an interface for outputting these data, for example:

struct data
{
    std::string url;
};

class uiface
{
public:
    virtual ~uiface() = 0;
    virtual void create_ui_element(const data&) = 0;
};

uiface::~uiface(){}

class framework
{
public:
    framework(): uiface_(0)
    {
        data dat;
        dat.url = “stdex.sf.net”;
        cont_.push_back(dat);
        dat.url = “nanaproject.wordpress.com”;
        cont_.push_back(dat);
    }

    void set(uiface * uif)
    {
        uiface_ = uif;
    }

    void work()
    {
        if(uiface_)
        {
            for(std::vector<data>::const_iterator i = cont_.begin(); i != cont_.end(); ++i)
            {
                uiface_->create_ui_element(*i);
            }
        }
}
private:
    uiface * uiface_;
    std::vector<data> cont_;
};

Now we need a GUI and it create some buttons for representing the data, and when a buttons is clicked, it outputs the data that button represents. Now, let’s define a GUI for that requirement.

namespace ui
{
    using namespace nana::gui;

    class bar
        : public form, public uiface
    {
    public:
        bar()
        {
            gird_.bind(*this);
        }
    private:
        typedef std::pair<std::shared_ptr<button>, data> btn_pair;

        //Now we implement the virtual function that declared by uiface
        virtual void create_ui_element(const data& dat)
        {
            btn_pair p(std::shared_ptr<button>(new button(*this)), dat);
            //Make the click event
            p.first->make_event<events::click>(*this, &bar::_m_click);
            p.first->caption(nana::stringset_cast(dat.url));
            gird_.push(*(p.first), 0, 22);
            ui_el_.push_back(p);
        }
    private:
        void _m_click(const eventinfo& ei)
        {
            //Check which button is clicked
            for(std::vector<btn_pair>::iterator i = ui_el_.begin(); i != ui_el_.end(); ++i)
            {
                if(i->first->handle() == ei.window)
                {
                    //Show data
                    i->second;
                    std::cout<<“open “<<i->second.url<<std::endl;
                    break;
                }
            }
        }
    private:
        gird gird_;
        std::vector<btn_pair> ui_el_;
    };
}

It’s done for implementing the GUI. Now, let’s make the framework work.

#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/button.hpp>
#include <nana/gui/layout.hpp>
#include <iostream>

include definition of framework…

int main()
{
    ui::bar bar;
    bar.show();

    framework fw;
    fw.set(&bar);
    fw.work();

    nana::gui::exec();
}

Run the program. Refer to figure 1, when we click a button, the program would output the data that is represented by button.

Figure 1 Design of a framework

Let’s rethink the implementation of class bar. It is a bit complicated, because a check of which button is clicked is needed. In fact, we can reduce the complexity of previous design by employing function object. Think about the following implementation.

class bar
    : public form, public uiface
{
    struct command
    {
        data dat;
        command(const data& d): dat(d)
        {}

        void operator()()
        {
            std::cout<<“open “<<dat.url<<std::endl;
        }
    };
public:
    bar()
    {
        gird_.bind(*this);
    }
private:
    //Now we implement the virtual function that declared by uiface
    virtual void create_ui_element(const data& dat)
    {
        std::shared_ptr<button> p(new button(*this));
        //Make the click event
        p->make_event<events::click>(command(dat));
        p->caption(nana::stringset_cast(dat.url));
        gird_.push(*p, 0, 22);
        ui_el_.push_back(p);
    }
private:
    gird gird_;
    std::vector<std::shared_ptr<button> > ui_el_;
};

As you see it, the new implementation is more convenient. The check of which button is clicked and the pair structure are removed. In C++11, the standard library provides a bind function. Generating a function object by using std::bind() instead of giving a explicit definition of a class that used for a function. Let’s remove the definition of struct bar::command and reimplement the create_ui_element().

virtual void create_ui_element(const data& dat)
{
    std::shared_ptr<button> p(new button(*this));
    //Make the click event
    typedef nana::functor<void()> fun; //or std::function<void()>
    p->make_event<events::click>(
            fun(std::bind(&bar::_m_click, this, dat))
            );
    p->caption(nana::stringset_cast(dat.url));
    gird_.push(*p, 0, 22);
    ui_el_.push_back(p);
}

void _m_click(const data& dat)
{
    std::cout<<“open “<<dat.url<<std::endl;
}

The member function _m_click() is very tiny, we can remove it with lambda expression in C++11.

The main idea of this section is binding the execution context at some place in code and retaining the context for future use. As above code shown, we bind the data with a function object, and when the function object is called, we could easily refer to the data. By using the reasonable technical idioms, we can make the code more slighter, more expressive, the benefit are: flexible application, less comment and easy maintenance.

Advertisements
Posted by: nanaproject | December 16, 2011

Preliminary Study of Nana C++ Library

Although C++ is a powerful and sytnax-agile language, in fact, many programmers don’t like to do GUI in C++, because it is so difficault to make GUI in C++. Some exsiting C++ GUI frameworks define their own rule that makes you write some stiff code for running, it always causes some problems, such as leaving your code in a deep inheritance hierarchy, making maintenance painful. Now, there is an alternative, Nana C++ Library, a pure C++ library that will guide you in doing GUI with your immense and extensive C++ knowledge/skill/idioms, it makes a great progress in doing GUI in C++.

Easy-to-Learn, Easy-to-Use
How easy to create a Hello World program with Nana?
#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/label.hpp>
int main()
{
    using namespace nana::gui;
    form fm;
    label lb(fm, 0, 0, fm.size().width, fm.size().width);
    lb.caption(STR(“Hello, World”));
    fm.show();
    exec();
}

Pretty easy, with understandable codes. Nana brings very simple and reasonable concepts to keep it easy. Secondly, Unlike other frameworks, they make you write stiff code due to name contstraints and syntax constraints. Nana can make your code more straightforward and readable. For example, answering an event.

#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/button.hpp>

void clicked(const nana::gui::eventinfo&)
{
    //When the window corresponds to fm is clicked,
    //this function will be “called”.
}

int main()
{
    using namespace nana::gui;
    form fm;
    fm.make_event<events::click>(clicked);
    fm.show();
    exec();
}

The name of clicked function is not name-constrained, you can give it any other name you want. It is more straightforward than implementing an event handler from inheritance. In some situations, we don’t need to care about the parameter of clicked() function, like the example.

void clicked() //NO parameter defined.
{
    //When the form is clicked,
    //this function will be “called”.
}

fm.make_event<events::click>(clicked); //Nana allows!

Very flexible, and keep your code simple. And this feature can be applied with function object.

What makes Nana so flexible?
Nana C++ Library does not contain any “extra compilers” to parse “special syntex”, Nana uses 100% C++ and the template techniques make this library so powerful and flexible. Nana is unlike other template-based library that causes a lot of code bloat and requires programmers have template-skilled, it’s newbie-friendly.

Nana is a complete C++ style library runs on Visual C++7.1/GCC 3.4 and later. If you were a C++ expert, Nana allows you to use lambda, a new feature of C++11, for event answering. Like this,

fm.make_event<events::click>([]{
        //When the form is clicked, the object created
        //by this lambda will be “called”.
    });

or

fm.make_event<events::click>([](const eventinfo& ei){
        //When the form is clicked, the object created
        //by this lambda will be “called”, and I can
        //read the parameter.
    });

Additionally, Nana would make code flexibile with std::bind in C++11.

Threading
Make easy, Nana is a thread-safe library and accessing a widget between threads is normalized. This is a great feature that makes programmer deliver the event answer to other thread easiler.

#include <nana/gui/wvl.hpp>
#include <nana/threads/pool.hpp>

void foo()
{
    //This function will be “called” in other
    //thread that is created by thread pool.
}

int main()
{
    using namespace nana::gui;
    using namespace nana::threads;

    pool thrpool;
    form fm;
    fm.make_event<events::click>(pool_push(thrpool, foo));
    fm.make_event<events::click>(pool_push(thrpool, []{
                //A lambda is also allowed.
            }));
    fm.show();
    exec();
}

RAII
There is a important feature as shown in above examples, as soon as a form object is created, its corresponding window is created, and the window is invisible till the show() is called to the form object, as soon as the form object is destructed, its corresponding window is closed, it conforms with the C++ object life-time concept.

Cross-Platform Programming
Nana C++ Library is designed to be used for cross-platform programming. Its frist release is under Windows. Now, the library is basiclly ported to Linux(X11).

The Most Important Feature: Free
Nana C++ Library is an open-source, it’s free for both commerical and non-commerical use.

Posted by: nanaproject | October 27, 2011

A new release of Nana C++ Library

Today, Nana C++ Library is made a new release, and this release is the frist release that supports for Linux(X11).

although this is a preview release for Linux, it makes a great progress!

A program built under Windows/Linux with Nana C++ Library.

Posted by: nanaproject | August 11, 2011

Programming with Nana C++ Library

A new guide to Programming with Nana C++ Library is released. This is a draft, wish you would like to programming with Nana C++ Library.

Posted by: nanaproject | June 11, 2011

A Method to prevent UI from blocking while busy

The GUI of Nana C++ Library(0.1.12 or later) is designed to save developers from the difficulties of threading, however, no UI framework will ever be able to provide a single-threaded solution for every sort of problem.

Most respondences to user interactions in events are finished immediately, it does not affect the responsiveness of the UI. The Nana C++ Library event model deals with events in sequence, this means the next event will be processed after current event is finished.

Consider the following example:

#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/button.hpp>
#include <nana/gui/widgets/progressbar.hpp>

class example
    : public nana::gui::form
{
public:
    example()
    {
        btn_start_.create(*this, 10, 10, 100, 20);
        btn_start_.caption(STR(“Start”));
        btn_start_.make_event<nana::gui::events::click>(*this, &example::_m_start);

        btn_cancel_.create(*this, 120, 10, 100, 20);
        btn_cancel_.caption(STR(“Cancel”));
        btn_cancel_.make_event<nana::gui::events::click>(*this, &example::_m_cancel);

        prog_.create(*this, 10, 40, 280, 20);
    }
private:
    void _m_start()
    {
        working_ = true;
        btn_start_.enabled(false);
        prog_.amount(100);
        for(int i = 0; i < 100 && working_; ++i)
        {
            nana::system::sleep(1000); //a long-running simulation
            prog_.value(i + 1);
        }
        btn_start_.enabled(true);
    }

    void _m_cancel()
    {
        working_ = false;
    }
private:
    bool working_;
    nana::gui::button btn_start_;
    nana::gui::button btn_cancel_;
    nana::gui::progressbar prog_;
};

int main()
{
    example ex;
    ex.show();
    nana::gui::exec();
    return 0;
}

This simple application simulates a long-running operation, and designed a button for start and a button for cancel the work. However, it is not difficult to imagine, _m_start() spends a long time to run while click the “Start” button, it does affect the responsiveness of the UI. The “Cancel” button does not work until the work is finish, only thing you can do is just wait.

Usually, the answer to handle the long-running operation in a separate thread, leaving the UI thread free to respond to user interface. When the long-running opertion is complete, it can report its result back to the UI thread for display.

Consider the following solution:

#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/button.hpp>
#include <nana/gui/widgets/progressbar.hpp>
#include <nana/threads/pool.hpp>

class example
    : public nana::gui::form
{
public:
    example()
    {
        btn_start_.create(*this, 10, 10, 100, 20);
        btn_start_.caption(STR(“Start”));
        btn_start_.make_event<nana::gui::events::click>(nana::threads::pool_push(pool_, *this, &example::_m_start));

        btn_cancel_.create(*this, 120, 10, 100, 20);
        btn_cancel_.caption(STR(“Cancel”));
        btn_cancel_.make_event<nana::gui::events::click>(*this, &example::_m_cancel);

        prog_.create(*this, 10, 40, 280, 20);

        this->make_event<nana::gui::events::unload>(*this, &example::_m_cancel);
    }
private:
    void _m_start()
    {
        working_ = true;
        btn_start_.enabled(false);
        prog_.amount(100);
        for(int i = 0; i < 100 && working_; ++i)
        {
            nana::system::sleep(1000); //a long-running simulation
            prog_.value(i + 1);
        }
        btn_start_.enabled(true);
    }

    void _m_cancel()
    {
        working_ = false;
    }
private:
    volatile bool working_;
    nana::gui::button btn_start_;
    nana::gui::button btn_cancel_;
    nana::gui::progressbar prog_;
    nana::threads::pool pool_;
};

int main()
{
    example ex;
    ex.show();
    nana::gui::exec();
    return 0;
}

The Nana C++ Library provides a threadpool class. To solute this problem, threadpool can help developer to get rid of thread managment, such as, how to create thread? how to wait for a thread finish? and so on.  Compare these tow pieces of code, they are very close, but the most important difference between these tow pieces of code is _m_start() is dispatched to the threadpool and execute in a background thread, the UI thread is not blocking and free to accept new events.

There is a function named pool_push, it creates a pool_pusher function object to push the _m_start() into threadpool. Registering the pool_pusher function object as a button event,  the pool_pusher function object will be called to push the _m_start() as a task into threadpool while clicking on the button.

In this version, the form makes an unload event also calling _m_cancel(), when closes the form, the application drops the rest of operations. But there is one question need to be answered, when the long-running operation is working, closing the form will closes the buttons and progressbar, and at the same time, the long-running operation is not finish synchronous, will crash the application when long-running operation calls methods of button and progressbar after closing the form? The anwser is YES, but the code above avoid the destruction of button and progressbar before the finish of _m_start(), the threadpool is defined following buttons and progressbar, this means the threadpool is destructed before the button and the progressbar, when destruct the threadpool, it waits until all worker thread is finish.

Handling a blocking operation with a background thread.

A long time blocking operation is usually uncancellable and not able to get the progress of process. In this situation, application usually updates UI to indicate it is working.

#include <nana/gui/wvl.hpp>
#include <nana/gui/widgets/button.hpp>
#include <nana/gui/widgets/progressbar.hpp>
#include <nana/threads/pool.hpp>

class example
    : public nana::gui::form
{
public:
    example()
    {
        btn_start_.create(*this, 10, 10, 100, 20);
        btn_start_.caption(STR(“Start”));
        btn_start_.make_event<nana::gui::events::click>(nana::threads::pool_push(pool_, *this, &example::_m_start));
        btn_start_.make_event<nana::gui::events::click>(nana::threads::pool_push(pool_, *this, &example::_m_ui_update));

        prog_.create(*this, 10, 40, 280, 20);
        prog_.style(false);

        this->make_event<nana::gui::events::unload>(*this, &example::_m_cancel);
    }
private:
    void _m_start()
    {
        btn_start_.enabled(false);
        nana::system::sleep(10000); //a blocking simulation
        btn_start_.enabled(true);
    }

    void _m_ui_update()
    {
        while(btn_start_.enabled() == false)
        {
            prog_.inc();
            nana::system::sleep(100);
        }
    }

    void _m_cancel(const nana::gui::eventinfo& ei)
    {
        if(false == btn_start_.enabled())
            ei.unload.cancel = true;
    }
private:
    nana::gui::button btn_start_;
    nana::gui::progressbar prog_;
    nana::threads::pool pool_;
};

int main()
{
    example ex;
    ex.show();
    nana::gui::exec();
    return 0;
}

When click on the start button, application pushs _m_start() and _m_ui_update() into threadpool. It is very easy!

Posted by: nanaproject | March 24, 2011

Restart the porting work

A few days ago, I started the project of porting the library from Windows to Linux. This work was stared as early as 2008, but it was interrupted in a little while, that because the core of library was not indefinite. Today the project is started again. After reading the code about the version for Linux that was written in 2008, I found it is difficult to reuse the code, there are serval details were changed. In fact, these are not problems, but the bigest challenge is porting the code from Windows to Linux. It is fortunate that when I was designing and implementing the version for Windows, I studied on similarity of features between two platforms. In another word, it is aimed to define a framework that can be easy to port to Windows and Linux. Although the framework is designed for cross-platform, the platform-specific features are still here, such as resource, bitmap/icon is for Windows and xpm is for Linux. Also, there are some features will be emulated in each platform. I can imagine that code without modification may be compiled for each platform one day.

Posted by: nanaproject | March 20, 2011

An application of Clone Method to Nana C++ Library

The clone method is widely used for user-defined renderers in Nana C++ library.

Motivation

A widget is a mini framewark providing a general method for users defining their own renderer. A common way looks like this.

class button_renderer
{
public:
virtual ~button_renderer() = 0;
virtual void render() = 0;
};

class button
{
public:
button()
: renderer_(new def_renderer) //class def_renderer: public button_renderer for default
{}

~button()
{
delete renderer_;
}

void set_user_renderer(button_renderer * r)
{
delete renderer_;
renderer_ = r;
}
private:
void _m_render()
{
renderer_->render();
}
private:
button_renderer * renderer_;
};

This is a typecial usage of strategy pattern that user can write a subclass from button_renderer. In this piece of code, programmers have to be faced with the hidden rule, the renderer object must to be allocated by new operator, it is invisible, not clear. for example.

user_def_renderer renderer;
button.set_user_renderer(&renderer);

button refers to a renderer which will be destroyed immediately. and the renderer object is not deleteable. there are two serious error.

and

user_def_renderer * r = new user_def_renderer;
button.set_user_renderer(r);

This will confuse programmers whether should delete pointee r after button is destroyed, if programmers don’t know what the button’s destructor to do.

A clone method can avoid these problems.

class button_renderer
{
public:
virtual ~button_renderer() = 0;
virtual void render() = 0;
virtual button_renderer * clone() const = 0;
};

class button
{
public:
void set_user_renderer(const button_renderer& r)
{
delete renderer_;
renderer_ = r.clone();
}
private:
button_renderer * renderer_;
};

the difference between two solutions is that button::renderer_ refers to duplicate renderer, not directly to user passed object. The clone method is used to avoid getting involved with the renderer object given by user, and const-reference of the parameter type of set_user_renderer is clear to programmer.

The Clone Method in Nana C++ Library

When a programmer wants to define an own renderer of slider, he have to write a subclass inherited from nana::gui::slider::renderer.

class my_slider_renderer: public nana::gui::slider::renderer
{
virtual void background(nana::gui::window, graph_reference, bool isglass);
virtual void adorn(nana::gui::window, graph_reference, const adorn_t&);
virtual void adorn_textbox(nana::gui::window, graph_reference, const nana::string&, const nana::rectangle&);
virtual void bar(nana::gui::window, graph_reference, const bar_t&);
virtual void slider(nana::gui::window, graph_reference, const slider_t&);
};

Then, set the renderer object for slider.

nana::gui::slider::renderer_cloneable<my_slider_renderer> rnd;
slider.ext_renderer(rnd);

or in one statement;

slider.ext_renderer(nana::gui::slider::renderer_cloneable<my_slider_renderer>());

When a programmer wants to extend the renderer that slider is in use, he should,

class my_extend_renderer: public nana::gui::slider::renderer
{
public:
my_extend_renderer(nana::pat::cloneable_interface<renderer>& reuse)
: reuse_(reuse.clone())
{
}

my_extend_renderer(const my_extend_renderer& rhs)
: reuse_(rhs.reuse_->clone())
{}

~my_extend_renderer()
{
reuse_->self_delete();
}

private:
virtual void background(nana::gui::window wd, graph_reference graph, bool isglass);
{
reuse_->refer().background(wd, graph, isglass);
//draw what i want.
}
virtual void adorn(nana::gui::window wd, graph_reference graph, const adorn_t& ad)
{
reuse_->refer().adorn(wd, graph, ad);
//draw what i want.
}

virtual void adorn_textbox(nana::gui::window, graph_reference, const nana::string&, const nana::rectangle&);
virtual void bar(nana::gui::window, graph_reference, const bar_t&);
virtual void slider(nana::gui::window, graph_reference, const slider_t&);
private:
nana::pat::cloneable_interface<renderer> * reuse_;

};

nana::gui::slider::renderer_cloneable<my_extend_renderer> rnd(slider.ext_renderer());
slider.ext_renderer(rnd);

In this piece of code, there is not a interface named clone in the interface class renderer, it avoid programmers getting involved with implementing for clone.

Posted by: nanaproject | September 9, 2008

A new widget

A listbox widget comes with Nana 0.1.5.

listbox

nana::gui::listbox

Now, it looks simple, but there are more than one thousand of lines of code to build it. -_-!

The Nana C++ library can make easy cross-platform programming, so the version of X11 is under construction. 🙂

peek of listbox on X11

peek of listbox on X11

There are many difficult points on implementation of X11 version 😦

« Newer Posts

Categories