Skip to content
junekey-jeon edited this page Jul 3, 2018 · 3 revisions

How to implement a button

Introduction

This article explain how to implement a button widget and some basic knowledge about nana::button. It is a simple widget and a good start point to learn the implementation of a widget. The implementation of nana::button is defined in "include/nana/gui/widgets/button.hpp" and "source/gui/widgets/button.cpp".

Nana.GUI provides the framework for implementing a widget. A GUI-widget is made of a window manipulator and a drawer trigger, so, to implement a widget we need to implement these two parts.

Window Manipulator

This class provides some operations that make the widget visible for the user. For example nana::button is a manipulator.

Drawer Trigger

A drawing operation is driven by the drawer trigger. It provied the internal of a widget, reacting to events and is invisible for an user. During the life time of a window manipulator and a drawer trigger, Nana.GUI does not involve creation and destruction of a drawer trigger.

Implementation

Window Manipulator
template<typename DrawerTrigger>
class basic_button: public widget_object<category::widget_tag, DrawerTrigger>
{public:
  basic_button(){}
  basic_button(nana::widget& widget, rectangle r)
  { 
    this->create(widget.handle(), r);
  }
};

This code defines the window manipulator of the button.

template<typename DrawerTrigger>

The template parameter DrawerTrigger is used to specify a drawer trigger.

basic_button inherit from widget_object<category::widget_tag, DrawerTrigger>. The widget_tag indicates the button is a Widget Window. Although all widgets are inherited from nana::widget, the widgets just can be inherited from the template class widget_object<>.

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 is made on a widget before its creation.

Drawer Trigger

A drawer trigger is more complicate than a window manipulator; it reacts to mouse and keyboard events. In this implementation of the button, the code for 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 nana::drawer_trigger.

using namespace nana;
class button_drawer: public nana::drawer_trigger
{public:
	button_drawer ();
	void bind_window(nana::widget & widget);
 private:
	void attached(nana::paint::graphics &gra ) override;
	void detached() override;
	void normal	(paint::graphics &gra) override;
	void mouse_leave(paint::graphics &gra, const arg_mouse &ei) override;
	void mouse_down	(paint::graphics &gra, const arg_mouse &ei) override;
	void mouse_up	(paint::graphics &gra, const arg_mouse &ei) override;
	void focus		(paint::graphics &gra, const arg_mouse &ei) override;
 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);

	nana::widget* widget_ ;
	bool 		is_ms_down_ ;
	bool 		is_focus_ ;
};

This button_drawer makes the constructor and the bind_window() member function public, and others private.

void bind_window(nana::widget& widget){widget_ = &widget;}

Nana.GUI creates a widget and tells the drawer trigger the widget through bind_window().

The private member functions not starting with _m_ are virtual functions that are defined by nana::drawer_trigger. These virtual functions are invoked inside the Nana.GUI. The name of some of these virtual functions starts with mouse_ because they are used for answering to mouse operations. You need to override these functions if you want some special capacities.

The private member functions starting with _m_ are defined by the button drawer trigger. They implements the drawing of the button. As will be readily seen, when a mouse event is raised, the drawer trigger calls these member functions for drawing the widget.

nana::widget * widget_;

This data member refers to a window manipulator which is connected with the drawer trigger.

bool is_ms_down_;

This indicates the current status of the left mouse button (is pressed down?).

Now, let's return to the details of the member functions with 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 by mouse_up(). is_focus_ will be modified by focus().

void button_drawer::set(nana::widget& widget)
{  if(widget_ == 0) widget_ = &widget;
}

Sets widget_, this function is invoked by the window manipulator's get_drawer_trigger(). The following member functions are the essential part of the drawer trigger.

void button_drawer::attached(nana::paint::graphics&)
{
	using namespace nana::API;
	is_ms_down_ = false;
	nana::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 the 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 from nana::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 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::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::API::unregister_drawer_event is responsible for destroying 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::API::refresh_window() will also invoke this normal() method. In this implementation, the normal() method calls the member functions that start with _m_ to operate 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::API::lazy_refresh();
}

When the mouse leaves the widget, Nana.GUI will call the mouse_leave, where the second parameter refers to an arg_mouse object which provides some event information. In this implementation we don't take care about this parameter. It's worth noting that nana::API::lazy_refresh() is invoked last, it 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. mouse_down(), mouse_up() and focus() are similar to mouse_leave(). Refer to "source/gui/widgets/button.cpp" for the details. Refer to Nana.Paint for the details of nana::paint::graphics.

Clone this wiki locally