Chapter 4. Word-wrapping labels

Index

Preserving window positions
Customizing visual appearance of display elements
Modifying an appearance object
Caching appearance objects
Hello World!

This example follows up on the hello world program. It uses a create_label() overload to create a label that word-wraps its text to the given width. A word-wrapping label gets created by setting x::w::label_config's widthmm value.

See the section called “Building example programs” for more information on building this example:

/*
** Copyright 2017-2019 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/config.H>

#include <x/w/main_window.H>
#include <x/w/main_window_appearance.H>
#include <x/w/screen_positions.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 <string>
#include <iostream>

// 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)
{
	x::w::gridlayoutmanager
		layout=main_window->get_layoutmanager();

	x::w::gridfactory factory=layout->append_row();

	x::w::label_config config;

	config.widthmm=100.0; // Initial text width is 100 millimeters

	// Optional parameter, alignment:
	config.alignment=x::w::halign::center;

	factory->create_label
		(
		 {
		  // x::w::rgb values specify the color of the following
		  // text. <x/w/rgb.h> declares several constants for standard
		  // HTML 3.2 colors, like x::w::blue:

		  x::w::blue,
		  "underline"_decoration,

		  // "name"_font - string literal
		  "liberation serif; point_size=24; weight=bold"_font,

		  "Lorem ipsum\n",

		  "no"_decoration,

		  // "name"_theme_font - font specified by the current theme/
		  //
		  // The "label" font is used for ordinary labels
		  "label; point_size=12"_theme_font,
		  x::w::black,

		  "dolor sit amet, consectetur adipisicing elit, "
		  "sed do eiusmod tempor incididunt ut labore et dolore mana "
		  "aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
		  "ullamco laboris nisi ut aliquip ex ea commodo consequat. "
		  "Duis aute irure dolor in reprehenderit in voluptate velit "
		  "esse cillum dolore eu fugiat nulla pariatur. "
		  "Excepteur sint occaecat cupidatat non proident, "
		  "sunt in culpa qui officia deserunt mollit anim id est "
		  "laborum."
		 }, config);
}

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

	auto close_flag=close_flag_ref::create();

	// Configuration filename where we save the window's position.

	std::string configfile=
		x::configdir("wordwraplabel@examples.w.libcxx.com")
		+ "/windows";

	// Load the saved window position.
	//
	// x::w::screen_positions captures the position and size of
	// main_windows. If the configuration file exists, the previously
	// captured positions and sizes of main_windows get loaded. Nothing
	// happens if the file does not exist.
	auto pos=x::w::screen_positions::create(configfile);

	// It's possible to capture more than one main_window's position and
	// size, and save it. Each main_window must have a unique label, that
	// identifies the stored position.
	//
	// Creating a new main_window with an optional screen_position and
	// a label ends up reopening the window (eventually, when it gets
	// show()n) in its former position and size (hopefully).
	//
	// This has no effect if no memorized position was loaded from the
	// the configuration (which includes the situation where the
	// configuration file does not exist).
	//
	// Passing an optional x::w::main_window_config as the first parameter
	// to main_window's constructor specifies optional main window settings.

	x::w::main_window_config config;

	// set_name_and_position() sets the main window's unique label, and
	// restores it from the specified x::w::screen_positions, if one was
	// saved
	//
	// NOTE: the screen_positions parameter that gets passed in here must
	// remain in scope and not get destroyed until the main window's
	// constructor returns.

	config.restore(pos, "main");

	// Obtain main window's appearance

	x::w::const_main_window_appearance appearance=config.appearance;

	x::w::rgb light_yellow
		{
		 x::w::rgb::maximum,
		 x::w::rgb::maximum,
		 (x::w::rgb_component_t)(x::w::rgb::maximum * .75)
		};

	// const_main_window_appearance is a cached constant object. It
	// remains referenced by the new main window, and the connection
	// thread can access it at any time.
	//
	// Thread safety is ensured by contract, because it is a constant
	// object. Modifying an appearance object involves invoking it's
	// modify() and passing it a closure, or a callable object.
	//
	// modify() makes a new copy of the appearance object and invokes
	// the closure with the new, temporarily-modifiable object
	// as its parameter. The closure can then modify its members.
	//
	// modify() returns a new modified appearance object, a
	// const_main_window_appearance once again, that, now safely constant
	// again, can replace the original config.appearance.

	config.appearance=appearance->modify
		([&]
		 (const x::w::main_window_appearance &custom_appearance)
		 {
			 custom_appearance->background_color=light_yellow;
		 });

	auto main_window=
		x::w::main_window::create(config, create_mainwindow);

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

	guard(main_window->connection_mcguffin());

	main_window->set_window_title("Hello world!");
	main_window->set_window_class("main", "wordwraplabel@examples.w.libcxx.com");
	main_window->on_delete
		([close_flag]
		 (THREAD_CALLBACK,
		  const x::w::busy &ignore)
		 {
			 close_flag->close();
		 });

	main_window->show_all();

	close_flag->wait();

	// Before terminating the most recent window position and save
	// needs to be saved into a screen_positions object:

	main_window->save(pos);

	// In this case we're using the initial screen_positions that were
	// loaded from the configuration file. Specifying an existing label
	// replaces the existing saved position with the same label.
	//
	// It's also possible to default-construct a new screen_positions,
	// and use it.
	//
	// Multiple main_windows must have a unique label, each.
	//
	// Finally, the screen_positions gets save()d into the configuration
	// file, for next time:

	pos->save(configfile);
}

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

Notes:

wordwraplabel.C also demonstrates several other formatting options for the text parameter:

Preserving window positions

Running wordwraplabel.C again should open its window in the same position where it was previously, This stickiness needs some additional coding, and the actual behavior can vary depending upon the display screen's window manager, the ultimate judge where new windows open and how big they are.

#include <x/config.H>
#include <x/w/screen_positions.H>

std::string configfile=
   x::configdir("wordwraplabel@examples.w.libcxx.com")
		+ "/windows";

x::w::screen_positions pos=x::w::screen_positions::create();

// ...

main_window->save(pos);
pos->save(configfile);

x::w::main_window's save() gets called before the program ends. This records the window's last known position on the screen in the x::w::screen_positions.

Note

It is possible to invoke save() from a callback, directly in response to a Quit menu option or in the window's on_delete() callback. A callback must forward its IN_THREAD handle, to an overloaded save() with an IN_THREAD signature, otherwise the thread will deadlock:

main_window->save(IN_THREAD, pos);
pos->save(configfile);

x::w::screen_positions object records the window's position, together with the sizes and properties of all adjustable containers and display elements:

More than one window's position may be recorded, and a unique label identifies each window with a recorded position. Each main window that gets created by the application (if there's more than one) needs a unique identifying label which serves as the window's identifier.

After recording each window's positions, x::w::screen_positions's save() saves all positions in a file. This example program uses LibCXX's base library's x::configdir function to initialize a configuration directory for wordwraplabel's use, where the window's position get saved.

x::w::screen_positions pos=x::w::screen_positions::create(configfile);

x::w::main_window_config config;

config.restore(pos, "main");

auto main_window=
    x::w::main_window::create(config,
                              [&]
                              (const x::w::main_window)
                              {
                                 // ...

Reopening a window in the same position is a three-step process:

  • Specifying a filename when create() a new x::w::screen_positions object. This reads the previously saved positions from the file, if the file exists.

  • The x::w::main_window_config objects is a helper object that sets optional main window settings. It gets constructed before the main window gets created, and its restore() method specifies and the application-assigned label for the new main window, and the loaded x::w::screen_positions object.

  • The x::w::main_window_config parameter gets passed to the overloaded main window create()or.

If the window had any save()d containers or display elements they have to be individually restored, as they get constructed by the creator lambda. All memorizable containers and display elements get automatically save()d together with their main window; but they need individual restoration.

The named window's position that was saved the last time wordwraplabel ran, that same position and size gets set for the new window. The same label gets used to record the window's position, by its save() method.

Note

It's possible that application windows still open in their last known position even without explicitly saving them this way. Some window managers try to remember application windows, and open them in the same location. It's also possible that an application's requested window position gets ignored by the window manager, and the application window still opens in some other location on the screen.