Chapter 16. The border layout manager

Index

Creating a border with an optional title
Border title and background
Background colors and titles

The border layout manager draws a border around a widget. The grid layout manager handles containers with many elements, and also has the means to draw borders around them. The grid layout manager has many options and settings, while the border layout manager is dedicated and optimized for this specific task, and is faster than the grid layout manager for this specific use case. The LibCXX Widget Toolkit uses the border layout manager to draw buttons and keyboard input focus borders.

The border layout manager has an option of adding a title to the border. This visually frames related widgets together and gives them a title.

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

#include "config.h"
#include <x/mpobj.H>
#include <x/appid.H>
#include <x/exception.H>
#include <x/destroy_callback.H>
#include <x/ref.H>
#include <x/obj.H>
#include <x/w/main_window.H>
#include <x/w/gridlayoutmanager.H>
#include <x/w/gridfactory.H>
#include <x/w/borderlayoutmanager.H>
#include <x/w/frame_appearance.H>
#include <x/w/label.H>

#include <string>
#include <iostream>
#include <sstream>
#include <utility>

#include "close_flag.H"

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

static void create_main_window(const x::w::main_window &mw)
{
	auto glm=mw->gridlayout();

	// Two sample containers with the border layout manager. One of them
	// will have a title.
	//
	// Even though the rest of the two containers are identical, the
	// title results in that one being slightly taller. For better
	// visual appearance, align all elements in row 0 at the bottom.
	glm->row_alignment(0, x::w::valign::bottom);

	x::w::gridfactory f=glm->append_row();

	x::w::new_borderlayoutmanager nblm;

	// The creator lambda that gets passed to create_container().
	// Get the creaed borderlayout() manager and call its replace()
	// to obtain a factory which we use to create_label() the
	// label inside the border.
	//
	// This is done for both sample containers, so we just define this
	// lambda once.

	auto creator=[]
		(const x::w::container &c)
	{
		c->borderlayout()->replace()
			->create_label("Border layout manager");
	};

	// We're just creating a simple label inside each border. Usually
	// an entire container gets created inside the border, but we just
	// have a label. Change the default padding between the border and
	// its element to 10 millimeters. This makes the container bigger,
	// for demo purposes.
	nblm.appearance=nblm.appearance->modify
		([]
		 (const auto &appearance)
		 {
			 appearance->hpad=10;
			 appearance->vpad=10;
		 });

	f->create_container(creator, nblm);

	// Create the 2nd one with the title. We can use the same
	// new_borderlayoutmanager, just set the title.

	nblm.title("Hello");

	f->create_container(creator, nblm);
}

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

	auto close_flag=close_flag_ref::create();

	auto main_window=x::w::main_window::create(create_main_window);

	main_window->set_window_title("Borders!");

	guard(main_window->connection_mcguffin());

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

	main_window->on_delete
		([close_flag]
		 (ONLY IN_THREAD,
		  const auto &ignore)
		 {
			 close_flag->close();
		 });

	main_window->show_all();

	x::mpcobj<bool>::lock lock{close_flag->flag};

	// update_title() updates the existing border's title.

	const char *titles[2]={"Hello", "World"};

	while (!lock.wait_for(std::chrono::seconds(1), [&] { return *lock; }))
	{
		std::swap(titles[0], titles[1]);

		// We want the container in row 0, column 1 of the main window's
		// grid.

		x::w::gridlayoutmanager glm=main_window->get_layoutmanager();

		x::w::container frame_with_border=glm->get(0, 1);

		// This container uses the border layout manager.
		x::w::borderlayoutmanager blm=
			frame_with_border->get_layoutmanager();

		// And update the border's title.
		blm->update_title(titles[0]);
	}
}

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

Creating a border with an optional title

Passing an x::w::new_borderlayoutmanager to a factory's create_container() creates a container with a x::w::borderlayoutmanager.

x::w::new_borderlayoutmanager's creates an empty container. The usual pattern is to have the creator lambda get the constructed container's border layout manager and use its replace() to obtain a factory that creates exactly one widget. But that widget can be another container that, for example, uses the grid layout mamanager. Thie results in a border around an entire container of widgets:

factory->create_container(
    [&]
    (const x::w::container &c)
    {
        auto f=c->borderlayout()->replace();

        x::w::new_gridlayoutmanager nglm;

        f->create_container([]
                               (const x::w::container &c)
                               {
                                    // ...
                               },
                              nglm);
    },
    x::w::new_borderlayoutmanager{}
);

This is the typical way to create a border. The creator lambda uses replace() to get the new contain's factory. The border layout manager always manages exactly one widget. Another widget created by the factory replaces, and takes the place of the previous one. The initial replace() installs the border layout manager's initial widget, and the lambda uses the factory to create a container that uses the grid layout manager, then proceeds to initialize the grid. The border layout manager draws a border around a single widget, but this widget could be a container of other widgets.

After setting any needed options in the x::w::new_borderlayoutmanager, create_container() creates the container with the border layout manager:

x::w::container c=f->create_container(creator, nblm);

create_container() takes the x::w::new_borderlayoutmanager and constructs a new container with the border layout manager, and calls the lambda to create the widget with the border. As always, create_container() invokes the creator lambda to initialize the contents of the new container. In the case of the border layout manager the creator lambda typically retrieves the borderlayout() to create the initial widget in the container.