Chapter 2. Hello world!

Index

Building example programs
Basic concepts
Application identifiers and version
Widgets are reference-counted objects with smart pointers
Containers own references to the widgets in the container
Proper reference-counting is critical
Connection threads and callbacks
Callbacks
Containers
Creator lambdas
Layout managers
Factories
Exceptions
Hello World!

The following hello world program displays the above application window.

/*
** Copyright 2017-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/image.H>
#include <string>
#include <iostream>

// Application identifier

// The application's main windows' positions and other internal settings
// are associated with its application identifier. Provide an explicit
// application identifier instead of using the default one that's derived
// from the program's filename.
//
// Applications may also declare an explicit x::appver() instead of using
// the default one that's derived from the program's timestamp. The saved
// main window positions are tied to the application identifier and version,
// and a different version removes all previously saved spositions. Explicit
// application versions should be updated with each application release.
// Once a window's or a dialog's position, or some widget-specific setting
// is saved it remains tied to its identifier even if its actual code is
// removed from the application and it no longer uses it. A versioned
// configuration serve to purge stale configuration data that's no longer
// applicable.

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

void testlabel()
{
	// Set up a destructor guard. The library automatically creates a
	// background thread to talk to the X server. When the main_window
	// object goes out of scope and gets destroyed, the connection thread
	// will stop, but not immediately. Some cleanup tasks must be done.
	//
	// This is not strictly necessary, but let's do a good job of
	// cleaning up after ourselves, to make sure that testlabel() does
	// not return until all display-related resources have been released
	// and the display server's connection thread stops.
	//
	// The guard object must be declared before the main_window object.

	x::destroy_callback::base::guard guard;

	auto close_flag=close_flag_ref::create();

	// The main application window.

	auto main_window=x::w::main_window
		::create(// Creator lambda, for the new main window
			 []
			 (const x::w::main_window &main_window)
			 {
				 // The creator lambda for the main application
				 // window.
				 //
				 // Since we specified a gridlayoutmanager
				 // for this window (see below), this is what
				 // get_layoutmanager() will give us, here.
				 //
				 // Alternatively,
				 //
				 // auto layout=main_window->gridlayout();
				 //
				 // does the same thing:
				 x::w::gridlayoutmanager
				     layout=main_window->get_layoutmanager();

				 // Center the display elements in column #0,
				 // the first (and the only) column in the grid
				 // that's about to get created.

				 layout->col_alignment(0, x::w::halign::center);

				 // The grid is currently empty. Append a
				 // new row to the grid, and return a factory
				 // that creates new elements in the new
				 // grid row.
				 x::w::gridfactory factory=
				     layout->append_row();

				 // Pass an optional label_config parameter
				 // to create_label().

				 x::w::label_config hello_world_label_config;

				 // Center the label's rows. This is
				 // an optional parameter, and defaults
				 // to x::w::halign::left. "right"
				 // is also an option (other halign
				 // values are not used by the label
				 // display element).

				 hello_world_label_config.alignment=
					 x::w::halign::center;

				 // Pad the next new grid element with
				 // an 8 millimeter border, and create an
				 // x::w::label element.
				 factory->padding(8.0).create_label({

						 // First label row
					"Hello world!\n",

						// Second label row, switch
						// to a non-default font.
					"liberation mono;point_size=40"_font,
					"Here I come!"
				     }, hello_world_label_config);

				 // Create a factory for the second row.
				 factory=layout->append_row();

				 // Also pad the new element
				 factory->padding(8);

				 // Create an image element.
				 //
				 // Load the dandelion-flower.jpg file from
				 // the examples directory.
				 //
				 // Must use "." to load images from the
				 // current directory. Filenames without a
				 // path component search the default theme
				 // directory, and are used to load theme
				 // icons.
				 factory->create_image("./dandelion-flower.jpg");
			 },

			 // Our main window will use the grid layout manager...

			 // ... but we already knew that. However, for
			 // completeness, this is the default value for the
			 // second parameter to create(), that specifies the
			 // grid layout manager for the new main window.
			 //
			 // This is why when the creator lambda gets invoked
			 // above, it expects to see a gridlayoutmanager.
			 // This parameter, if specified, must be a subclass
			 // of x::w::new_layoutmanager, and this is one such
			 // subclass.

			 x::w::new_gridlayoutmanager{});

	// If the connection to the display server goes away, we don't have
	// many options...
	//
	// For this simple program, close_flag->close(), like the more
	// orderly on_delete() does, will also work.
	//
	// _exit() is for emergencies. Can't use exit() because the library
	// will try to wait for its internal execution thread to finish, but
	// it'll still be running (but doing nothing of importance).

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

	// Retrieve the mcguffin for the underlying display connection, and
	// guard it. We won't return from this function until the underlying
	// display connection completely goes away.

	guard(main_window->connection_mcguffin());

	// Set our title.
	main_window->set_window_title("Hello world!");

	// Set the window manager class instance and resource identifiers.
	// This sets the internal WM_CLASS window property that some window
	// managers use for window-specific settings.
	//
	// The default resource identifier is set to the window's name,
	// which defaults to "main" (unless overridden when the main_window
	// gets created), and to the appid(), so these values are the default
	// ones:

	main_window->set_window_class("main",
				      "helloworld.examples.w.libcxx.com");

	// Install a callback lambda that the connection thread invokes when
	// it gets a "close window" message from the display server.
	main_window->on_delete
		([close_flag]
		 (ONLY IN_THREAD,
		  const x::w::busy &ignore)
		 {
			 close_flag->close();
		 });

	// Show the main window, and all of its display elements.
	main_window->show_all();

	// Wait until the close window message is received.
	close_flag->wait();
}

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

Building example programs

The above program comes from the examples directory. The GNU toolchain (autoconf, automake, and gmake) is required to build LibCXX Widget Toolkit's example programs, as explained in the section called “Developing applications with the LibCXX Widget Toolkit:

cd examples
autoreconf
./configure
make