Skip to content
Jinhao edited this page May 7, 2017 · 6 revisions

Basic Usage

There are examples illustrate the basic usage for creating and displaying a menu.

#include <nana/gui.hpp>
#include <nana/gui/widgets/menu.hpp>

int main()
{
	using namespace nana;

	//Define a handler for menu item.
	auto handler = [](menu::item_proxy& ip) {
		msgbox msg("Menu Clicked");
		msg<<ip.text();
		msg.show();
	};

	//Define a menu with 2 items
	menu m;
	m.append("Menu Item 0", handler);
	m.append("Menu Item 1", handler);

	//Define a form
	form fm;
	fm.events().mouse_up([&](const arg_mouse& arg){
		//When release the mouse right button, display the pop-up menu
		//at the position of cursor.
		if (mouse::right_button != arg.button)
			return;
		
		m.popup_await(fm, arg.pos.x, arg.pos.y);
		//or
		//m.popup(fm, arg.pos.x, arg.pos.y);
	});

	fm.show();
	exec();
	return 0;
}

Another example: using menu as an local variable.

int main()
{
	using namespace nana;

	form fm;
	fm.events().mouse_up([](const arg_mouse& arg){
		if (mouse::right_button != arg.button)
			return;
		
		auto handler = [](menu::item_proxy& ip) {
			msgbox msg("Menu Clicked");
			msg << ip.text();
			msg.show();
		};

		menu m;
		m.append("Menu Item 0", handler);
		m.append("Menu Item 1", handler);

		m.popup_await(arg.window_handle, arg.pos.x, arg.pos.y); //Good
		//or
		//m.popup(arg.window_handle, arg.pos.x, arg.pos.y); //Bad
	});

	fm.show();
	exec();
	return 0;
}

In the second example, the menu is defined as a local variable. If displays the menu by calling menu::popup, the menu window will be closed immediately, even never see a menu window displayed. Because menu::popup doesn't block until the menu window gets closed, the menu will be destructed when exits the mouse-up event handler.

Sub Menu

There are to ways to create sub menu for an item.

//1, Set an existing menu as a sub menu.
menu popup_menu;
popup_menu.append("Hello");

menu existing_menu;
existing_menu.append("World");

//Sets the existing_menu as a sub menu for popup_menu.
popup_menu.link(0, existing_menu);
//2, Create a sub menu.
menu popup_menu;
popup_menu.append("Hello");

auto sub = popup_menu.create_sub_menu(0);
sub->append("World");

To retrive the sub menu of a certain item, use menu::link.

auto sub = menu.link(1);
//It returns a pointer to the sub menu of the 2nd item.

Shortkey

The menu sets a shortkey with a character behind the first of &-character in text for an item.

menu.append("File(&F)");
menu.append("&File");

Modifying Appearance

Gap between Menus

The gap of menu is used to specify the pixels of gap between the menu and its sub menus.

Gap of Menus

int main()
{
	using namespace nana;

	menu main_menu;
	main_menu.append("Item 0");
	main_menu.append("Item 1");

	main_menu.gaps({ 3, -2 }); //Sets gaps

	menu * submenu = main_menu.create_sub_menu(0);
	submenu->append("Item 0");
	submenu->append("Item 1");

	form fm;
	fm.events().mouse_up(menu_popuper(main_menu));
	fm.show();

	exec();
}

Customizing Renderer

When a renderer is set for a menu, it also affects all sub menus. An example to customize a renderer and make the menu semi-transparent in Windows.

Example

The customized renderer will employ an existing menu renderer,

#include <windows.h> //introduces Win32 APIs

class renderer : public nana::menu::renderer_interface
{
	using color_rgb = ::nana::color_rgb;
public:
	renderer(const nana::pat::cloneable<renderer_interface>& rd)
		: reuse_(rd) //Reuses the current renderer
	{}
private:
	void background(graph_reference graph, nana::window wd) override
	{
		graph.rectangle(true, nana::colors::white);
		graph.rectangle(false, static_cast<color_rgb>(0x5DC1AC));

		//Makes the menu transparent, it only works under Windows with #include <windows.h>
		HWND native = reinterpret_cast<HWND>(nana::API::root(wd));
		DWORD ex_style = ::GetWindowLong(native, GWL_EXSTYLE);
		::SetWindowLong(native, GWL_EXSTYLE, ex_style | 0x00080000 /*WS_EX_LAYERED*/);

		using slwa_t = BOOL(WINAPI*)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
		slwa_t slwa = reinterpret_cast<slwa_t>(::GetProcAddress(::GetModuleHandleA("User32.DLL"), "SetLayeredWindowAttributes"));
		if (slwa)
			slwa(native, 0, 220, 0x00000002/*LWA_ALPHA*/);
	}

	void item(graph_reference graph, const nana::rectangle& r, const attr & atr) override
	{
		if (state::active == atr.item_state)
			graph.rectangle(r, true, static_cast<color_rgb>(0x9ADCCA));
	}

	void item_image(graph_reference graph, const nana::point& pos, unsigned image_px, const nana::paint::image& img) override
	{
		reuse_->item_image(graph, pos, image_px, img);
	}

	void item_text(graph_reference graph, const nana::point& pos, const std::string& text, unsigned pixels, const attr& atr) override
	{
		reuse_->item_text(graph, pos, text, pixels, atr);
	}

	void sub_arrow(graph_reference graph, const nana::point& pos, unsigned pixels, const attr & atr) override
	{
		reuse_->sub_arrow(graph, pos, pixels, atr);
	}
private:
	nana::pat::cloneable<renderer_interface> reuse_;
};

int main()
{
	using namespace nana;

	menu main_menu;
	main_menu.append("Item 0");
	main_menu.append("Item 1");

	main_menu.gaps({ 3, -2 }); //Sets gaps
	main_menu.renderer(renderer(main_menu.renderer())); //Reuses the current renderer
	main_menu.item_pixels(20);

	form fm;
	fm.events().mouse_up(menu_popuper(main_menu));
	fm.show();
	exec();
}