BuGUI
BUtton Grid User Interface
Loading...
Searching...
No Matches
BuGUI

License: LGPL v3

A library intended to facilitate user interface design on creative hardware, especially button grid controllers. Our approach is to consider these devices as very low resolution touch screens, with some additional controls potentially (buttons, encoders, actual screens). To that effect, the library is structured as a simplistic yet general-purpose UI system. This effort to keep our implementation generic makes it easily extensible and eventually capable of covering a wider range of both hardware interfaces and software use cases.

Motivation

The interaction button grids afford is closely tied to the usage of hardware step sequencers and drum machines. However, various innovative implementation enable a greater variety of interfaces in different products. These interfaces are in most cases specific to a particular hardware, or software.

BuGUI acts as an abstraction layer between software and hardware. We propose a generic approach to hardware interfaces, where devices are essentially considered in the same way that screen, mouse, and keyboard are in regular GUI development. Ultimatley, BuGUI enables custom hardware integration for any software, available on every supported devices.

Quick Overview

To create a BuGUI widget, simply inherit from either bugui::widget or bugui::container_widget. A container widget can contain other widgets, which can be added with the function add_widget<my_child_widget>(...).

Painting

A custom BuGUI widget can be painted on button grid when it defines a paint(painter&) function:

struct my_struct : widget<my_struct>
{
void paint(painter& p)
{
// vertical white line along the left side of the widget
p.set_color(255, 255, 255, 255); // R, G, B, A
p.draw_line(0, 0, 0, get_height());
// from_x, from_y, to_x, to_y
}
...

Handling inputs

Widgets can respond to user input on their position. The functions on_press, on_double_press , on_drag and on_drop can be defined to handle each type of interaction. These functions allow for retrieving data from the presser object, such as the desired value type:

void on_press(presser& p)
{
bool on_off{p.get_bool()};
auto midi{p.get_midi()};
auto value{p.get_value()};
// generic floating point value [0., 1.]
}

or the input's coordinates:

void on_double_press(presser& p)
{
auto x{p.get_x()};
auto y{p.get_y()};
}
void on_drag(presser& p)
{
auto tx{p.get_target_x()};
auto ty{p.get_target_y()};
}

Additional functions can be defined in controller class can be defined to handle input from specific controls outside the button grid. For example, handling input from a device's labelled buttons, such as Play, Stop, Shift ...etc.

class my_controller final : contrller<my_controller>
{
...
void on_Play(bugui::presser& p);
void on_Stop(bugui::presser& p);
void on_Shift(bugui::presser& p);
...
Definition presser.hpp:14

Device support

Device specifications are written in plain c++ classes. No includes or inheritance is required here. Here is a snipet from the Launchpad_Pro_Standalone class, enabling support for the Novation Launchpad Pro in standalone mode:

struct Launchpad_Pro_Standalone final
{
static consteval auto c_name() { return "Launchpad_Pro_Standalone"; }
static consteval auto port_name() { return "Launchpad Pro Standalone Port"; }
...
// Available buttons
struct buttons
{
enum cc{};
// top row
static consteval uint8_t Up_id() { return 91; };
static consteval uint8_t Down_id() { return 92; };
...
struct grid
{
enum on_off{};
static consteval uint8_t width() { return 8; }
static consteval uint8_t height() { return 8; }
static consteval auto ids()
{
return std::array<std::array<uint8_t, width()>, height()>
{
std::array<uint8_t, width()>{81, 82, 83, 84, 85, 86, 87, 88}
, std::array<uint8_t, width()>{71, 72, 73, 74, 75, 76, 77, 78}
, std::array<uint8_t, width()>{61, 62, 63, 64, 65, 66, 67, 68}
...
struct color
{
static consteval auto palette()
{
using namespace std;
return array<pair<unsigned long, uint8_t>, 128>
{
pair<unsigned long, uint8_t>{0, 0}
, pair<unsigned long, uint8_t>{1973790, 1}
, pair<unsigned long, uint8_t>{8355711, 2}
...

Acknowledgments

The research conducted in this publication was funded by the Irish Research Council under grant number EPSPG/2024/1176. It is supervised by Victor Lazzarini, Gordon Delap and Jean-Michaƫl Celerier. Many thanks to them, as well as Raphael Marczak and Simon Archipoff for their continued support!