ControlBase docs

ControlBase was my attempt to create an ultra-simple but reasonably-featured GUI widget library. It fits into a single source file (controls.cpp) and is self-contained except:

Basically all ControlBase does is define some objects which: describe the position and content of various on-screen widgets; and implement some methods encapsulating their behavior. Since it does not itself handle either drawing or (for the most part) events, in theory it could be easily divorced from OpenGL or even SDL.

Below are docs for the classes described in slice.h and controls.h.

About Slices

“Slice” is a simple class taken from Jumpman describing “something like a sprite” (get it? … never mind). It basically just holds a 2D array of 32-bit pixel values. It knows how to populate that 2D array from a png, and it knows how to perform a one-time “construct” operation (which could mean anything a subclass decides it means– in the included texture_slice class, it means uploading a texture). I only really included it in Jumpcore because ControlBase uses it for button icons.

slice

fields:

methods:

texture_slice

Has everything “slice” does plus a field:

Calling “construct” on a texture_slice will turn the currently held image into an OpenGL texture indexed by “texture”. It will also add itself to a “reconstruct_registry” hash; the object will be automatically removed from the hash (and its texture destroyed, if any) on deletion. Main.cpp goes through the reconstruct_registry each time the window resizes and tells each object to construct() again (since all textures will have been lost when the surface resized).

For OpenGL reasons, any png loaded into texture_slice must be an NxN square where N is a power of two.

About ControlBase

ControlBase consists of two families of classes: Containers and Controls. Controls are things you interact with– labels and buttons and such. Containers decide where and how those controls are placed on the screen; they also keep track of controls, and delete them when the container itself is destroyed. Using ControlBase basically consists of:

ControlBase could be more flexible than it is: All Containers in the current version of Jumpcore behave something like a column, and all buttons have the same dimensions (given at the top of controls.cpp as button_verts).

ContainerBase

Parent class for all containers

fields:

methods:

ColumnContainer : ContainerBase

Like ContainerBase, only instead of a column of big controls it displays two parallel columns of smaller controls– since each such control is about the size of an icon this makes a good starting point for something like a tool palette.

ScrollContainer

Like ContainerBase, only it displays a maximum of SCROLLMAX buttons at a time and (if needed) displays up to two “scrollbar” buttons which provide access to the additional controls.

fields:

methods:

ControlBase

Parent class for all Controls. Has the full ability to display however any other control does, but lacks interactivity. If you want to make something like a “button” you basically want to subclass ControlBase and override Click or Wheel.

fields:

methods:

KeyboardControl : ControlBase

A ControlBase item that knows how to accept keyboard events.

fields:

methods:

TextControl : KeyboardControl

A control that acts like a text entry box.

fields:

WheelControl : TextControl

A TextControl that holds a numeric value, and can be “scrolled” by using the mousewheel or arrow keys. You can also enter a value manually but it will check whatever you entered to make sure it’s a number within bounds.

fields:

methods:

SelectControl : WheelControl

A WheelControl that acts like a “Menu”– it rounds “is” to the nearest integer and displays a string from a list with that index.

fields:

methods:

CheckControl : ControlBase

A toggle button or check box.

fields:

methods:

CheckGroup

Tracks a number of CheckControls such that (a) it enforces only one of the controls added to it may be checked at a time and (b) at any time you can find out which checkbox in the group is selected, if any.

fields:

methods:

AN EXAMPLE

Here’s a fairly short piece of sample code that makes use of basically every single feature in ControlBase:

	// A normal-type column of controls, in this case serving as a dubious little calculator app:
	ContainerBase *basicWidgets = new ContainerBase(uiSpace, CRIGHT);
	columns.push_back(basicWidgets);

	// A custom "button":
	class CalculatorControl : public ControlBase { public:
		CalculatorControl(string _text) : ControlBase(_text, true) {}
		KeyboardControl *in1;
		SelectControl *op;
		WheelControl *in2;
		ControlBase *out1;
		virtual void click() {
			float v1 = atof(in1->text.c_str());
			float v2 = in2->is;
			int selected = op->is + 0.5; // Round to index
			float result = 0;
			switch(selected) {
				case 0: result = v1 + v2; break;
				case 1: result = v1 - v2; break;
				case 2: result = v1 * v2; break;
				case 3: result = v1 / v2; break;
			}
			ostringstream s; s.precision(3);
			s << result; out1->text = s.str();

			ERR("%f, %f = %f\n", v1, v2, result);
		}
	};
	CalculatorControl *go = new CalculatorControl("click me");

	// And what the heck, let's give the "click me" button an icon:
	go->img = new texture_slice();
	go->img->init(8,8); // Normally you'd load an image out of a file. I'm just gonna draw one right here:
	for(int y = 0; y < 8; y++) {
		for(int x = 0; x < 8; x++) {
			bool there = y > 0 && y < 7 && x > 0 && x < 7 && y != 3 && y != 4; // Trace an = sign
			go->img->pixel[x][y] = there ? 0xFF : 0; // 0xFF for opaque black, 0 for transparent
		}
	}
	go->img->construct();

	// A text field:
	go->in1 = new TextControl("0");

	// A "select control" / menu:
	vector<string> ops; ops.push_back("+"); ops.push_back("-"); ops.push_back("*"); ops.push_back("/");
	go->op = new SelectControl(ops, 0, false);

	// A wheel control:
	go->in2 = new WheelControl(0, -200, 200, 1);

	// A simple text label:
	go->out1 = new ControlBase();

	basicWidgets->add(go->in1);
	basicWidgets->add(go->op);
	basicWidgets->add(go->in2);
	basicWidgets->add(go);
	basicWidgets->add(go->out1);

	basicWidgets->commit();

	// A two-column block of buttons, acting like a palette of some kind.
	ContainerBase *palette = new ColumnContainer(uiSpace, CLEFT);
	columns.push_back(palette);

	static CheckGroup checkGroup;

	// Some toggle-button-type checkboxes-- six of them, in a group, so that only one can be picked at once.
	for(int c = 0; c < 8; c++) {
		string label; label += ('a' + c);
		palette->add( new CheckControl( label, false, &checkGroup ) );
	}

	palette->commit();

	// A container with a scrollbutton and too many buttons to fit on screen at once
	ContainerBase *scroll = new ScrollContainer(uiSpace, CMIDL);
	columns.push_back(scroll);

	// Some text labels:
	for(int c = 0; c < 26; c++) {
		string label = "| "; label += ('a' + c); label += " |";
		scroll->add( new ControlBase(label) );
	}
	scroll->commit();