Chapter 31. Progress bars

Progress bar

An x::w::progressbar is a specialized container with a layout manager, the grid layout manager by default. It's expected that the contents of this widget report an outgoing progress of some sort of a multi-step process; typically using a descriptive label.

The progress bar itself draws a sliding swath of color, starting from the left margin, and growing until it reaches the right margin, indicating the completion of the process.

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

#include "config.h"
#include <x/mpobj.H>
#include <x/exception.H>
#include <x/destroy_callback.H>
#include <x/ref.H>
#include <x/obj.H>
#include <x/appid.H>

#include <x/w/main_window.H>
#include <x/w/label.H>
#include <x/w/gridlayoutmanager.H>
#include <x/w/gridfactory.H>
#include <x/w/progressbar.H>

#include "close_flag.H"

#include <string>
#include <iostream>

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

// Create the progress bar.

static auto initialize_progressbar(const x::w::factory &f)
{
	// progressbar_config is an optional parameter to create_progressbar().
	//
	// It provides the progress bar's initial value and maximum value,
	// as well as a custom appearance object.

	x::w::progressbar_config config{0, 100};

	auto pb=f->create_progressbar
		([]
		 (const x::w::progressbar &pb)
		 {
			 // We are creating the progress bar with the
			 // grid layout manager. Put one element into the
			 // progress bar, our label.

			 auto glm=pb->gridlayout();

			 auto f=glm->append_row();

			 // Position the label centered, in the progress bar.

			 f->halign(x::w::halign::center);
			 f->create_label("0%")->show();
		 },

		 // Optional parameter: initial configuration
		 config,

		 // Optional parameter: the grid layout manager, by default.

		 x::w::new_gridlayoutmanager{});

	pb->show();

	return pb;
}

void showprogressbar()
{
	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)
			 {
				 auto layout=main_window->gridlayout();
				 x::w::gridfactory factory=
					 layout->append_row();

				 initialize_progressbar(factory);
			 });

	// Retrieve the progress bar, and the label elements from the newly-
	// created main_window.

	// Both the main window and the progress bar containers are grid
	// layout managers, with one display element: row 0, column 0.

	x::w::progressbar pb=main_window->gridlayout()->get(0, 0);

	x::w::label l=pb->gridlayout()->get(0, 0);

	main_window->set_window_title("Progress bar!");

	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};

	// Count the progress bar, and go away.

	int v=0;

	while (1)
	{
		lock.wait_for(std::chrono::milliseconds(500),
			      [&] { return *lock; });
		if (*lock) break;

		if (v >= 100)
			break;
		v += 5;

		// Update the progress bar to reflect the completion
		// percentage.
		//
		// It's up to us to update the label inside the progress bar.
		// We format "x%" ourselves, and update the label.
		//
		// Progress bar's update() method updates the progress bar's
		// visual slider. The third parameter to update() is an
		// optional closure. The closure parameter is an optimization
		// technique.
		//
		// Updating the label redraws it, and updating the
		// progress bar ends up redrawing the label again, to include
		// the slider that "slides" under the label. Passing a closure
		// to update() executes the closure and updates the progress
		// bar in one operation, redrawing the whole thing in one shot.

		std::ostringstream o;

		o << v << '%';

		pb->update(v, 100, [txt=o.str(), l]
			   (ONLY IN_THREAD)
			   {
				   l->update(txt);
			   });
	}
}

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

progressbar.C uses a factory's create_progressbar()'s to create a new progress bar, then creates a label inside it. progressbar.C then proceeds to count from 0 to 100. At each step, progressbar.C updates the label's text to read n%, and calls the progress bar's update() method to draw the progress bar's slider.

update() takes two integer values, and an optional closure. The total progress of the progress bar ranges from 0 to maximum_value (the 2nd parameter to update()) with value (the 1st parameter) indicating the current level of progress.

Essentially, update() draws the slider from the left margin and up to value/maximum_value of the progress bar's width. Both the value and maximum_value get updated by the same update(). Each update() provides the same maximum_value, and an increasing value, but each update may specify a different maximum_value, and a higher or a lower value. The progress bar's slider simply gets moved to the recalculated position. A value of 0 does not show the slider at all, and slider covers the entire width of the progress bar when both values are equal.

progressbar.C also gives an example of using the third, optional, update() parameter, for optimization purposes.