Skip to content
RSLLES edited this page Jan 24, 2023 · 20 revisions

Event is a messaging mechanism provided by the window system. Various kinds of events are generated by the window system for notifying the application. A key or mouse event is generated when the user presses or releases a key or mouse button, and the application can receive the event and response to user actions.

Different window systems provide different patterns of messaging mechanism. Nana implements an abstract pattern of event mechanism to hide the differences between different window systems.

Registering and Unregistering an Event

To response to the click action generated by any specified button, we need to make the click event for the button.

#include <nana/gui/wvl.hpp> 
#include <nana/gui/widgets/button.hpp> 
void foo() { }
void barclick(const nana::arg_click& am) { }
void barmouse(const nana::arg_mouse& am) { }
int main()
{
	using namespace nana;
	form fm;
	fm.events().click(foo);
	fm.events().click    (barclick);
	fm.events().dbl_click(barmouse);
	fm.show();
	exec();
}
 

As shown in above code, the event() is a member function of the abstract class widget, implemented using specific virtual functions of each widget type and which return a reference to the general_events member object of the widget object. We then select one of it basic_event template members (in this case click and dbl_click, of type basic_event< arg_click > and basic_event< arg_mouse >) and for it we call the template call operator(handler) which set a handler for the event, that is, it is used to register an event handler. The handler have to be a callable object which take void (like foo) or one argument of the same type as the template argument (in this case arg_click and arg_mouse, like barclick). The above code makes one click and one dbl_click events for the form object. When the user clicks the body of the form, Nana is responsable for invoking the function foo() registered as a handler of the specified event. Additionally, the function foo() can be specified with a parameter of type const nana::arg_mouse& to receive the information about the event. The details of eventinfo are described in section 6.2.

The click() returns a handle of event handler if the registration is successful. The type of the returned value is nana::event_handle. With the handle, we can delete the event handler manually. For example:

	event_handle handle = fm.events().click(foo); 
	fm.umake_event(handle); 

After calling the umake_event(), the foo() function would not be invoked any more when the user clicks the body of form. In most situations, we would not take care of deleting the event handler, because the event handlers will be deleted while form is closed.

Stopping Event Propagation

Alternative to operator call: connect(), connect_front(), connect_unignorable(), length(), emit(), clear(), connect(), remove (event_handle evt) override,

All the event argument types have a method called stop_propagation(), when it is called in a chain of event handler, the ignorable handlers behind the current one in the chain will not get called.

int main()
{
    using namespace nana;

    form fm;

    fm.events().click([]{
        //It will get called, but it is not the first event handler to be called.
    });

    fm.events().click([](const arg_mouse& arg){
        arg.stop_propagation();
    });

    fm.events().click([]{
        //It will not get called.
    });

    fm.events().click.connect_unignorable([]{
        //It will get called because it is unignorable.
    });

    fm.events().click.connect_front([]{
        //It will get called firstly, because it is the beginning of the chain.
    });

    fm.show();
    exec();
}

Events

The class general_events provides near 20 named fundamental events that every widget owns. An event type is a template member of general_events of type basic_event< arg_type > that correspond to an specific event which is generated by Nana. For every event we can set handlers, which are callable objects that can take none or one argumet of a type derived from event_arg containing the specific information related to the event type. This information is sent to the application through a (const) reference to an event_arg object in event handler.

	#include <nana/gui/wvl.hpp> 
	void foo(const nana::arg_mouse& a_m) 
	{ 
		//Refer to a_m for information of event. 
	} 
	int main() 
	{ 
		using namespace nana; 
		form fm; 
		fm.events().click(foo); 
		fm.show(); 
		exec(); 
	} 

Nana.GUI provides some raw events, such as click, mouse_move and so on. Most of these events can work with all widgets implemented by Nana.GUI, but some of them are individual, like unload for root widget and elapse for timer.

Every widget has an interface for registering an event. It uses events() to return a reference to the widgetΒ΄s member general_events. From then we select one of the events and call the template call operator to set the event handler. This template function calls specifies as template parameter the corresponding event_arg type of the abstract pattern of event mechanism . The events are:

Event Argument Description
click arg_click A mouse click event that occurs after mouse_down and before mouse_up
dbl_click arg_mouse A mouse double click event.
mouse_enter arg_mouse A mouse enters a widget.
mouse_move arg_mouse A mouse moves over a widget.
mouse_leave arg_mouse A mouse leaves a widget.
mouse_down arg_mouse A mouse button is pressed on a widget.
mouse_up arg_mouse A mouse button is released on a widget.
mouse_wheel arg_wheel A mouse scrolls the wheel on a widget.
expose arg_expose the visibility changes.
focus arg_focus A widget's focus is changed.
mouse_dropfiles arg_dropfiles the mouse drops some external data while the window enable accepting files
key_press arg_keyboard A key is pressed on a focus widget.
key_release arg_keyboard A key is released on a focus widget.
key_char arg_keyboard A character, whitespace or backspace is pressed
shortkey arg_keyboard The widgets received a defined shortkey.
move arg_move The widgets changes position
resizing arg_resizing A widget's size is changing.
resized arg_resized A widget's size is changing.
destroy arg_destroy A widget is about to be destroyed but occurs when all children have been destroyed
unload ? A form is closed by clicking the X button, only works for root widget.
elapse ? A widget received a tick that is sended by timer.
mouse_drop ? A mouse release over a window that is registered as recipient of drag and drop. deprecated??

An user-defined event function may have a parameter of type const nana::event_arg& for queering the event information, such as mouse position.

	void foo();
	void foo_with_parameter(const nana::arg_mouse&);
	class user_def_functor
	{ public:
	   void operator()(const nana::arg_mouse&); 	//user-defined function must have the parameter.
	};
	nana::button().events().click(foo);
	nana::button().events().click(foo_with_parameter);
	nana::button().events().click(user_def_functor());

click() returns a handle which may be used to uninstall the associated user-defined event function. Nana.GUI destroys the user-defined event function automatically when the widget is beginning to be destroyed.

This just describes these raw events, but some widgets like nana::treebox provides some high-level events, such as expanding a node. These details are only described in its reference.

For different events, the event_arg contains different structures. The following paragraphs describe the details of event_arg.

Mouse Events

The mouse is an important and convenient input device. The user controls the mouse cursor on the screen through moving and clicking the mouse. If the mouse cursor moves over a widget, the system would generate a mouse_move event to notify the program to which the widget belongs. The structure of arg_mouse contains:

	struct arg_mouse : public event_arg
	{
		event_code evt_code;  
		::nana::window window_handle;  ///< A handle to the event window
		::nana::point pos;             ///< cursor position in the event window
		bool left_button;              ///< mouse left button is pressed?
		bool mid_button;               ///< mouse middle button is pressed?
		bool right_button;             ///< mouse right button is pressed?
		bool shift;                    ///< keyboard Shift is pressed?
		bool ctrl;                     ///< keyboard Ctrl is pressed?
	};

The structure of mouse wheel event contains:

	struct arg_wheel : public arg_mouse
	{
		enum class wheel{
			vertical,
			horizontal
		};

		wheel	which;		///< which wheel is rotated
		bool	upwards;	///< true if the wheel is rotated to the top/left, depends on which and false otherwise
		unsigned distance;	///< expressed in multiples or divisions of 120
	};

In an event handler function, we can refer to the structure by using event_arg or some other base argument.
For example:

void foo(const nana::arg_mouse& ae) 
{ 
    using namespace nana; 
    switch(ea.evt_code) 
    { default:            
                          //refers to the structure of mouse events in arg_mouse. 
             break; 
    case evt_code::mouse_wheel: 
                         //refers to the structure of mouse_wheel event. 
            break; 
    } 
} 

or:

void foo(const nana::event_arg& ea) 
{ 
    using namespace nana; 
   if      (dynamic_cast<arg_wheel&> (ea) 
   {              //refers to the structure of mouse_wheel event.   
   }
   else if (dynamic_cast<arg_mouse&> (ea)  	 
   {    	  //refers to the structure of mouse events.        
   }
} 

To receive a mouse event, register the event with the specific type.

click/dbl_click 

When the user clicks the mouse and the mouse cursor is in a widget, the widget receives a click event that is generated by Nana.

mouse_enter/mouse_move/mouse_leave 

This event is sent to a widget when the mouse cursor enters/leaves the widget.

mouse_down/mouse_up 

This event is sent when the user presses/releases/moves the mouse while the mouse cursor is in a widget.

Keyboard Events

There are four different kinds of keyboard events in Nana C++ Library: key_press, key_release, key_char and shortkey.

A window system usually uses an input focus to represent a window which would receive the keyboard events. In general, a window which user clicks would be set to the input focus widow in Nana C++ Library. Additionally, the program can determinate which window gets the input focus by calling nana::API::set_focus() .

The structure of keyboard event contains:

   struct arg_keyboard : public event_arg
   {
   	event_code evt_code;	        ///< it is event_code::key_press in current event
   	::nana::window window_handle;	///< A handle to the event window
   	mutable nana::char_t key;	///< the key corresponding to the key pressed
   	mutable bool ignore;	        ///< this member is not used
   	bool ctrl;	                ///< keyboard Ctrl is pressed?
   	bool shift;	                ///< keyboard Shift is pressed
   };

As the definition shown, the member key and ignore are defined as mutable, the feature will be explained later.

key_press/key_release 

When the users hit the keyboard, the key_press would be generated when a key is pressed down, and the mouse_release would be generated when the pressed key is released.

key_char 

The key_char event is an abstract event. A window system usually translates the keys into characters. For example, to type a Chinese character one usually needs to hit in the keyboard more than one key and the window system translates these keys into a Chinese character and then a key_char event is generated and sent to the program. The two members, key and ignore, are defined as mutable in the structure of key event. It is used to modify the state of key_char event. During key_char processing, if the member ignore is set to true, Nana will igore the key. For example, when a program is designed to receive the number input, the program should test the key in key_char event, and set the ignore to true if the input char is not a digital. Like in the below code:

   void only_digital_allowed(const nana::arg_keyboard& ak) 
   { 
   	ak.ignore = (ak.key < '0' || ak.key > '9'); 
   }

Function Objects

This chapter shows two basic concepts: function objects and lambdas.
A Function Object, or Functor (the two terms are synonymous) is simply any object that can be called as if it is a function. It can be an object of a class that defines a member function operator().

The function object is an impressive technology. A function object is a more general concept than a function because a function object can have state that persist across several calls and can be initialized and examined from outside the object, unlike a static local variable. For example:

	class sum 
	{ public: 
		sum() : i_(0) {} 
		operator int() const volatile { return i_; } 
					   //this makes the objects of this class "callable" like a function. 
		void operator()(int x) volatile { i_ += x; } 
	  private: 
		int i_; 
	}; 

	void foo(const std::vector<int>& v) 
	{ 			//gather the sum of all elements. 
	   std::cout << std::for_each(v.begin(), v.end(), sum{} ) << std::endl; 
	} 

std::for_each() returns a copy of the object sum, and we are able to retrieve the state or result. On the basis of the feature that function objects retain its own state, it is easy used for concurrency process, and it is extensively used for providing flexibility in the library implementation.

Nana C++ Library uses a large number of function objects to make the framework work. To make the framework flexible enough, Nana C++ Library uses the general std::function class template.

By using the functor class template, we are able to make Nana C++ Library get rid of types that involved. For example:

    #include <nana/gui/wvl.hpp> 
    #include <iostream> 
    void foo() 
    { 
        //std::system("cls"); 
        std::cout<<"foo"<<std::endl; 
    } 
    int bar() 
    { 
        //std::system("cls"); 
        std::cout<<"bar"<<std::endl; 
        return 1;
    } 
    void foo_with_eventinfo(const nana::arg_mouse& ei) 
    { 
      std::cout << "foo_with_eventinfo, mouse pos = ("
       << ei.pos.x  << ", "  <<  ei.pos.y  << ")" << std::endl; 
    } 
    class click_stat 
    { public: 
        click_stat(): n_(0), id(++glob_id) { } 
        click_stat(const click_stat&o):n_(o.n_), id(++glob_id) {} ; 

        void respond   ()   { std::cout<<"click_stat     "<<id<< " = "<<++n_<<std::endl; } 
        int  respond_I ()   { std::cout<<"click_stat int "<<id<< " = "<<++n_<<std::endl; return 1;} 
        void respond_ei(const nana::arg_mouse& ei) 
        { 
             std::cout << "click_state "<<id<< " width eventinfo = " << ++n_ 
            << ", mouse pos = ("  <<ei.pos.x<<", "<<ei.pos.y<<")"
            << std::endl; 
        } 
     private: 
        //click_stat(const click_stat&) ; 
       int n_;
       const int id; 
       static int glob_id ;
    }; 
    int click_stat::glob_id{0};

    int main() 
    { 
        using namespace nana; 
        using fun_t            = std::function<void(      void      )>  ; 
        using fun_with_param_t = std::function<void(const arg_mouse&)>  ; 

        form        fm; 
        click_stat  cs; 
    
        fm.events().click(foo); 
        fun_t       f=foo; 
        fm.events().click(f); 
        fm.events().click(bar); 

        fm.events().click(foo_with_eventinfo); 
        fun_with_param_t fp(foo_with_eventinfo); 
        fm.events().click(fp); 

        fm.events().click( [&cs](){cs.respond();}                  ); 
        f= std::bind( &click_stat::respond, cs); 
        fm.events().click(  f                                      ); 
        fm.events().click( std::bind( &click_stat::respond  , &cs) ); 
        fm.events().click( [& ](){cs.respond();}                   ); 
        //fm.events().click( std::bind( &click_stat::respond_I, &cs) ); 
        fm.events().click( [& ](){cs.respond_I();}                   ); 


        fp= std::bind( &click_stat::respond_ei, &cs , std::placeholders::_1 ); 
        fm.events().click(   fp                                                            ); 
        fm.events().click( [&](const arg_mouse&ma){cs.respond_ei(ma);}                     ); 
        fm.events().click( std::bind( &click_stat::respond_ei, cs, std::placeholders::_1)  ); 

        fm.show(); 
        exec(); 
    } 

There are four different types of event handlers that can be processed. It is flexible and reduce the complexity of study and use.

![Figure 2.1 Various methods to make events to respond click.] (https://raw.githubusercontent.com/qPCR4vir/nana-demo/master/Examples/events.png)

In the previous example, we illustrated the use of std::function and the flexibility of using a function object. Practically, creating a functor object is not required. Using these functions this way instead of creating a functor: ????? example:

	int main() 
	{ 
		using namespace nana; 
		form fm; 
		click_stat cs; 

		fm.events().click( foo                                      ); 
		fm.events().click( std::bind( &click_stat::respond,    &cs) ); 
		fm.events().click( foo_with_eventinfo                       ); 
		fm.events().click( std::bind( &click_stat::respond_ei, &cs) ); 

		fm.show(); 
		exec(); 
	} 

Predefined Function Objects (deprecated - ?)

Nana C++ Library includes many different predefined function objects. Using these function objects together with function templates increases the expressive power of the library as well as making the resulting code much more efficient. For example, if a C++ program wants to close the form when the form is being clicked.

form.events().click(destroy(form)); 

Please include <nana/gui/functional.hpp> before using these function objects.

	class destroy 
	{ public: 
		destroy(nana::window wd); 
		void operator()() const; 
	}; 

Destroy the window.

	class hide 
	{ public: 
		hide(nana::window wd); 
		void operator()() const; 
	}; 

Hide the window.

	class show 
	{ public: 
		show(nana::window wd); 
		void operator()() const; 
	}; 

Show the window.

The Lambda Expression

A lambda expression is a mechanism for specifying a function object. The lambda is a new feature that introduced into C++ language recently, the primary use for a lambda is to specify a simple action to be performed by some functions. For example:

	#include <nana/gui/wvl.hpp> 
	#include <iostream> 
	int main() 
	{ 
		nana::form form; 
		form.events().click( 
			[]{ std::cout<<"form is clicked"<<std::endl; } 
			); 
		form.show(); 
		nana::exec(); 
	} 

The argument []{ std::cout<<"form is clicked"<<std::endl; } is a "lambda" (or "lambda function" or "lambda expression") in C++ language(C++11). A lambda starts with plain [], and compound-state block {} defines a function body. In fact, A lambda defines an anonymous function object, and therefore a lambda could be invoked through a function-call syntax.

	[]{ std::cout<<"hello, Nana"<<std::endl; }(); 

The use of lambda is creating an anonymous function object and so the arguments should be specified. For example:

	form.events().click( 
			[](const nana::arg_mouse& ei) 
			{ std::cout<<"mouse pos=("<<ei.pos.x<<", "<<ei.pos.y <<std::endl;} 
		); 

The lambda-declarator () is used like a parameter-list. Let's stop the introduction to the lambda, if you want more details of lambda, please refer to other C++ books.

Clone this wiki locally