Chapter 25. Context popup menus

Index

Creating the contents of the context popup menu
Creating new context popup menus on demand
Keyboard shortcuts for context popup menus
Context popup menu

Each widget can have an associated context popup menu. Clicking on it with the right pointer button makes the context popup menu appear next to the pointer's current location.

Setting up a context popup menu is a two step process:

The executed callback has no obligation to do anything in particular, such as displaying a popup menu. All that happens is that it gets executed by the pointer's right button getting clicked with the pointer position on top of the widget. popupmenu1.C demonstrates the expected behavior: the callback simply show()s the context popup menu.

/*
** Copyright 2018-2021 Double Precision, Inc.
** See COPYING for distribution information.
*/

#include "config.h"
#include "close_flag.H"

#include <x/exception.H>
#include <x/destroy_callback.H>
#include <x/appid.H>

#include <x/w/main_window.H>
#include <x/w/gridlayoutmanager.H>
#include <x/w/gridfactory.H>
#include <x/w/label.H>
#include <x/w/text_param_literals.H>
#include <x/w/font_literals.H>
#include <x/w/container.H>
#include <x/w/listlayoutmanager.H>

#include <string>
#include <iostream>

std::string x::appid() noexcept
{
	return "popupmenu1.examples.w.libcxx.com";
}

// This is the creator lambda, that gets passed to create_mainwindow() below,
// factored out for readability.

void create_mainwindow(const x::w::main_window &main_window)
{
	auto layout=main_window->gridlayout();
	x::w::gridfactory factory=layout->append_row();

	// Besides the right alignment, set the vertical alignment of the
	// label to middle, it looks better.

	auto label=factory->padding(3).create_label({
			"liberation sans; point_size=20"_font,
				"Right click on me, please"
				});

	// Create a popup menu. The menu items get created just like they
	// get created for an ordinary menu, using the list layout manager.

	x::w::container popup_menu1=label->create_popup_menu
		([]
		 (const x::w::listlayoutmanager &lm)
		 {
			 lm->append_items({
					 [](ONLY IN_THREAD,
					    const auto &info)
					 {
						 std::cout << "New"
							   << std::endl;
					 },
					 "New",

					 [](ONLY IN_THREAD,
					    const auto &info)
					 {
						 std::cout << "Open"
							   << std::endl;
					 },
					 "Open",

					 [](ONLY IN_THREAD,
					    const auto &info)
					 {
						 std::cout << "Close"
							   << std::endl;
					 },
					 "Close",
				 });
		 });

	// Install a callback for the context popup action, right button
	// click. The callback captures the menu popup, by reference, and
	// show()s it.

	label->install_contextpopup_callback
		([popup_menu1]
		 (ONLY IN_THREAD,
		  const x::w::element &my_element,
		  const x::w::callback_trigger_t &trigger,
		  const x::w::busy &mcguffin)
		 {
			 popup_menu1->show();
		 });
}

void popupmenu1()
{
	x::destroy_callback::base::guard guard;

	auto close_flag=close_flag_ref::create();

	auto main_window=x::w::main_window::create
		([&]
		 (const auto &main_window)
		 {
			 create_mainwindow(main_window);
		 });

	main_window->on_disconnect([]
				   {
					   _exit(1);
				   });

	guard(main_window->connection_mcguffin());

	main_window->set_window_title("Click the label for a popup");
	main_window->on_delete
		([close_flag]
		 (ONLY IN_THREAD,
		  const x::w::busy &ignore)
		 {
			 close_flag->close();
		 });

	main_window->show_all();

	close_flag->wait();
}

int main(int argc, char **argv)
{
	try {
		popupmenu1();
	} catch (const x::exception &e)
	{
		e->caught();
		exit(1);
	}
	return 0;
}

Note

The context popup menu does not own a reference on its widget, and the widget does not own a reference on its context popup menu, but all other rules for captured references apply. The callback's first parameter is a reference to its widget, and this is done in a manner that does not violate the rules. Context popup callbacks often need to use their widgets, and this avoids the chore of weakly-capturing their widgets, and recovering the strong reference in the callback.

In this example, the context popup gets captured by value, by the installed callback. The installed callback exists as long as the context popup remains installed: as long as its widget exists, or until a replacement context popup callback gets installed (only one context popup callback exists for each widget), or until remove_contextpopup_callback() explicitly removes it.

Creating the contents of the context popup menu

create_popup_menu()'s parameter is a creator lambda whose job is to create the contents of the popup menu. The menu gets created the same way as normal application menus. The lambda receives the list layout manager as its parameter, and proceeds to create the menu items.

create_popup_menu() returns an opaque container that represents the popup menu. show() it makes it appear next to the current pointer location.

It is possible to modify the contents of the context popup menu, using get_layoutmanager() to obtain its list layout manager, and using the list layout manager's methods. However, since a context popup menu's creation is not dependent on the context popup callback, it is possible to simply create a new context popup menu, on demand.