Nana - a C++ framework project
The Programmer's Guide |
How to implement a button 1. Introduction This article is indented to explain how to implement a button widget and some basic knowledge through nana::gui::button. nana::gui::button is a simple widget and it is a good start to learn the implementation of a widget. the implementation of nana::gui::button is defined in "include/nana/gui/widgets/button.hpp" and "source/gui/widgets/button.cpp". Nana.GUI provides a framework for implementing a widget. A graphical user interface widget is made of a window manipulator and a drawer trigger, so implementing a widget is needed to implement this tow parts. Window Manipulator It is a class provides some operations, it is a visible for an user, example. nana::gui::button is a manipulator. Drawer Trigger A drawing operation is driven by the drawer trigger, it is provied for the internal of a widget, it is invisible for an user. During the life time of a window manipulator and a drawer trigger, Nana.GUI does not involve the creation and destroying of a drawer trigger. 2. Implementation 2.1 Window Manipulator template<typename DrawerTrigger> class basic_button: public widget_object<category::widget_tag, DrawerTrigger> { public: basic_button(){} basic_button(nana::gui::widget& widget, int x, int y, unsigned width, unsigned height) { this->create(widget.handle(), x, y, width, height); } }; These code is defined the window manipulator of the button. template<typename DrawerTrigger> The template parameter DrawerTrigger is used for specifying a drawer trigger. basic_button is inherited from widget_object<category::widget_tag, DrawerTrigger>, the widget_tag indicates the button is a Widget Window. Although all widgets are inherited from nana::gui::widget, the widgets just can be inherited from the template class widget_object<> while define. The default constructor does not create the widget unless the create() method is called. The second constructor will create the widget by calling the create() method. Nana.GUI guarantees the safety if an operation of a widget before its creation. 2.2. Drawer Trigger A drawer trigger is more complicated than a window manipulator, it reacts a mouse or a keyboard event. In this implementation of the button, the code of drawing the button is written inside the drawer trigger directly. now, let's dip into the definition of the drawer trigger. A drawer trigger must be inherited from std::gui::drawer_trigger. class button_drawer: public nana::gui::drawer_trigger<cagetory::widget_tag> { public: button_trigger(); void bind_window(nana::gui::widget&); private: void attached(nana::paint::graphics&); void detached(); void normal(paint::graphics&); void mouse_leave(paint::graphics&, const eventinfo&); void mouse_down(paint::graphics&, const eventinfo&); void mouse_up(paint::graphics&, const eventinfo&); void focus(paint::graphics&, const eventinfo&); private: void _m_draw_title(paint::graphics&, bool is_mouse_down, bool is_focus); void _m_draw_background(paint::graphics&, bool is_mouse_down); void _m_draw_border(paint::graphics&, bool is_mouse_down); private: nana::gui::widget* widget_; bool is_ms_down_; bool is_focus_; }; This button_drawer makes the constructor and a bind_window() member function as public, and others are private. void bind_window(nana::gui::widget& widget) { widget_ = &widget; } Nana.GUI creates a widget and tell the drawer trigger the widget through bind_window(). The private member functions start with non-_m_ are virtual functions that are defined by nana::gui::drawer_trigger, these virtual functions are invoked inside the Nana.GUI, some of these virtual functions start with mouse_ are used for answering a mouse operation. It was needed override these functions you want some special capacities. The private member functions start with _m_ are defined by the button drawer trigger, it implements the drawing of the button. As will be readily seen, when a mouse event raised, the drawer trigger calls the member functions start with _m_ drawing the widget. nana::gui::widget * widget_; This data member refers to a window manipulator which is connecting with the drawer trigger. bool is_ms_down_; This indicates the current mouse status if the left button is pressed down. Now, let's return to the details of member functions start with _m_. button_drawer::button_drawer() :widget_(0), is_ms_down_(false), is_focus_(false) {} The default constructor initializes the data members. is_ms_down_ will be modified by mouse_down() and mouse_up(). is_focus_ will be modified by focus(). void button_drawer::set(nana::gui::widget& widget) { if(widget_ == 0) widget_ = &widget; } Sets widget_, this function is invoked by window manipulator's get_drawer_trigger(). The following member functions are the essential of the drawer trigger. void button_drawer::attached(nana::paint::graphics&) { using namespace nana::gui::API; is_ms_down_ = false; nana::gui::window window = widget_->handle(); make_drawer_event<events::mouse_leave>(window); make_drawer_event<events::mouse_down>(window); make_drawer_event<events::mouse_up>(window); make_drawer_event<events::focus>(window); } When a widget is creating, Nana.GUI is responsible for attaching the drawer trigger which is returned by the window manipulator to the Window Manager of Nana.GUI, at the same time, Nana.GUI calls the attach() method provided by the drawer trigger, in other words, the attach() method is the first method of a drawer trigger that Nana.GUI calls. When the drawer trigger is attached to the Window Manager, the connection is created between the drawer trigger and window manipulator that the drawer trigger will receive the callback of an event from a widget. In this member function, button_drawer registers the events through the handle of window manipulator. The drawer trigger has a special function for registering an event(it is different with nana::gui::API::register_event and make_event() of a window manipulator). Register an event, and Nana.GUI calls back the corresponding function for answering the event. In button_drawer's attach(), it registers mouse_leave, mouse_down and mouse_up, you can register more events if you want button more specially good effect. The attach() has a parameter, it refers to a nana::paint::graphics object, this object is the off-screen buffer of the widget, any drawing operation on this object, it will be displayed on the monitor. This object is created and destroyed by Nana.GUI. There is not any operation on this object. void button_drawer::detached() { nana::gui::API::unregister_drawer_event(widget_->handle()); } When a drawer trigger is detached from the Window Manager, Nana.GUI will callback the detach() method. In the button_drawer implementation, we just unregister the events that we registered in attach(). nana::GUI::API::unregister_drawer_event is responsible for destroying the all registered events for the specified window. void button_drawer::normal(paint::graphics& ghc) { _m_draw_background(ghc, is_ms_down_); _m_draw_border(ghc, is_ms_down_); _m_draw_title(ghc, is_ms_down_, is_focus); } The normal() method defined by a drawer trigger will be invoked after a widget is created completely, and nana::gui::API::refresh_window() will also invoke this normal() method. In this implementation, the normal() method calls the member functions start with _m_ for operating the graphics object. void button_drawer::mouse_leave(paint::graphics& ghc, const eventinfo&) { _m_draw_background(ghc, false); _m_draw_border(ghc, false); _m_draw_title(ghc, false, is_focus_); nana::gui::API::lazy_refresh(); } When the mouse is leave from the widget, Nana.GUI will call the mouse_leave, the second parameter refers to an eventinfo object which provides some event information, in this implementation we don't take care about this parameter. It's worth noting that a nana::gui::API::lazy_refresh() is invoked at last, the function will let the graphics object display on monitor. lazy_refresh() only works in an event callback, hence we can't find it in normal() method. In a similar way, mouse_down(), mouse_up() and focus() is same as mouse_leave(). Refering to "source/gui/widgets/button.cpp" for the details. Refering to Nana.Paint for the details of nana::paint::graphics. Return |