TcMenu supports a wide range of rendering devices, from HD44780 based units using our LiquidCrystal fork through to mono OLED’s and full colour TFT displays using the Adafruit_GFX library.
In order to achieve such a wide range of displays the renderer describes things in terms of menu items, active positions, editors and dialogs. This allows each renderer to convert these concepts into something that works for it’s needs.
You can also easily take over the display to draw your own screen at any time. This is such a large subject, it deserves a page of its own.
Type: BaseMenuRenderer
in BaseRenderers.h
Most renderers are based on the same base class, and have common functionality.
See the reference documentation for base renderer
Type: BaseDialog
in BaseDialog.h
It is also possible to present a simple dialog for either information, or Yes / No question onto almost all supported displays. Along with this you can optionally decide to let the dialog show on any remote connections, taking over the top of the display similar to an Alert Box.
Firstly, if we are interested in the outcome of the dialog, we must give the dialog a function to call us back upon when complete:
//
// this method is called when the dialog is dismissed.
//
void onDialogFinished(ButtonType btnPressed, void* /*userdata*/) {
if(btnPressed != BTNTYPE_OK) {
// do something if OK was pressed.
}
}
Next we get hold of the dialog and initialise it. Here we choose a dialog with OK and CANCEL as options:
BaseDialog* dlg = renderer.getDialog();
dlg->setButtons(BTNTYPE_OK, BTNTYPE_CANCEL, 1);
Now we call the show method to make the dialog take over the screen
dlg->show(pgmHeaderText, remoteAllowed, onDialogFinished); // true = shows on remote sessions.
Lastly we can copy some text into the second line of the dialog (not program memory):
dlg->copyIntoBuffer("Hello");
For all displays including LiquidCrystal we support the concept of title widgets. Title widgets allow a small icon to be presented in the title area that can have a number of states. A non exhaustive list of examples of this:
Here’s an example OLED display showing two widgets:
Each TitleWidget
has an array of icons that represent the states
. Each icon should first be defined:
const uint8_t iconData1[] PROGMEM = { 0, 0, 0 etc };
const uint8_t iconData2[] PROGMEM = { 0, 0, 0 etc };
Following this, we then define the array of icons:
const uint8_t* const iconsData[] PROGMEM = { iconData1, iconData2 };
Lastly we then define a TitleWidget
that represents this icon state:
TitleWidget iconsWidget(iconsData, numOfIcons, width, height [, &optionalNextWidget]);
The optionalNextWidget is a pointer to the next widget, if you only have one you don’t provide it.
Now we set the first widget as follows:
renderer.setFirstWidget(&iconsWidget);
To change the state of a widget, simply call it’s setter method:
iconsWidget.setCurrentState(iconNumZeroBased);
There are default icons for both WiFi strength and connection state included that work for a wide range of displays.
Icons for low resolution displays such as Nokia 5110:
#include "stockIcons/wifiAndConnectionIcons8x7.h"
Icons for higher resolution displays such as TFT and OLED:
#include "stockIcons/wifiAndConnectionIcons16x10.h"
Icons for 5x8 LiquidCrystal / HD44780 displays:
#include "stockIcons/wifiAndConnectionIconsLCD.h"
In each file there are two icon sets defined.
// a set of wifi icons, 0 is not connected, 1..4 are low to high strength
const uint8_t* const iconsWifi[] PROGMEM = { iconWifiNotConnected, iconWifiLowSignal, iconWifiMedSignal, iconWifiStrongSignal, iconWifiBestSignal };
// a boolean not connected (0) or connected (1) icon
const uint8_t* const iconsConnection[] PROGMEM = { iconDisconnected, iconConnected };
In TcMenu 2.0 we have added the ability to override the way that renderers draw in several ways. Firstly, lets start with a discussion of what’s possible.
This flexible configuration based rendering is made possible by a display factory. The display factory stores all the grids, icons and drawing properties in high performance btree lists that are optimised for reading. We’ll go through each below but first there’s a couple of types you will come across often that we’ll discuss here.
Along with this overview, please see the reference documentation for more complete details, they will not be repeated here:
Stores an X and Y coordinate in a 32bit bit-packed struct. It supports copy construction and assignment. You can directly access x
and y
.
Coord myCoord(10, 4); // create a coord with x=10 and y=4
Coord copyCoord(myCoord); // copy myCoord into copyCoord
int x = myCoord.x;
int y = myCoord.y;
Works similar to padding in HTML, You can directly access the top
, bottom
, left
, right
values.
MenuPadding equalPadding(4); // all sides have padding of 4
MenuPadding equalPadding(top, right, bottom, left); // per side padding.
We can draw a menu item at any grid and row position, just be careful that the row definition comes before the natural menu row, otherwise the row will already be taken and can’t be overridden. There are three ways to draw with this type of renderer:
Let’s say we want menuBtn1
and menuBtn2
in a single row, here’s how we do it, further let’s say that we want them to justify center without the value being presented:
// We define grid positions for menuBtn1 and menuBtn2, with a grid width of 2.
factory.addGridPosition(&menuBtn1, GridPosition(GridPosition:: DRAW_TEXTUAL_ITEM,
GridPosition::JUSTIFY_CENTER_NO_VALUE, 2, 1, 4, 45));
factory.addGridPosition(&menuBtn2, GridPosition(GridPosition:: DRAW_TEXTUAL_ITEM,
GridPosition::JUSTIFY_CENTER_NO_VALUE, 2, 2, 4, 45));
In order to be able to render as an icon we first need to tell our display properties about the icon, we do this by registering an icon with an association to a particular menu item. Let’s say for example that we had a sub-menu called menuSettings
that we wanted to draw as an icon. First we would add the icon to the cache:
// step 1, get the graphics factory
auto & factory = renderer.getGraphicsPropertiesFactory();
// step 2, add an icon to the image cache
Coord iconSize(32, 32);
factory.addImageToCache(DrawableIcon(menuSettings.getId(), iconSize, DrawableIcon::ICON_XBITMAP,
settingsIconData, optionalSelectedIcon));
// step 3, tell the renderer that the settings icon should be in a grid of three, column 1, 45 high, drawn as icon
factory.addGridPosition(&menuSettings, GridPosition(GridPosition::DRAW_AS_ICON_ONLY,
GridPosition::JUSTIFY_CENTER_NO_VALUE, 3, 1, 4, 45));
You can also change the font, color, padding, size and default justification at three levels:
To change the defaults use setDrawingPropertiesDefault
on the factory, providing the component type and the drawing settings.
To change the properties for all in a submenu use setDrawingPropertiesForItem
on the factory, providing the component type and drawing settings.
To change the properties for a single item use setDrawingPropertiesAllInSub
on the factory, providing the component type and drawing settings.
Here is an example that provides an override for a specific item, it is overridden to display as a title, provides a custom palette, padding, spacing, font and height:
color_t specialPalette[] { ILI9341_WHITE, ILI9341_RED, ILI9341_BLACK, ILI9341_BLUE};
factory.setDrawingPropertiesForItem(ItemDisplayProperties::COMPTYPE_TITLE, menuStatus.getId(), specialPalette,
MenuPadding(4), nullptr, 4, 10, 30,
GridPosition::JUSTIFY_CENTER_WITH_VALUE );