/*
The MIT License (MIT)
library writen by Kris Kasprzak
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
On a personal note, if you develop an application or product using this library
and make millions of dollars, I'm happy for you!
rev date author change
1.0 1/2022 kasprzak initial code
2.0 1/2022 kasprzak added touch support
3.0 2/2023 kasprzak modified lib to work with mech and touch
*/
#include "ILI9341_t3_Menu.h"
#include <ILI9341_t3.h> // fast display driver lib
EditMenu::EditMenu(ILI9341_t3 *Display, bool EnableTouch) {
d = Display;
// I no longer require you to choose mehcanical or touch
// the library now supports both, I'm
// keeping the arg to keep implementations from breaking
}
void EditMenu::init(uint16_t TextColor, uint16_t BackgroundColor,
uint16_t HighlightTextColor, uint16_t HighlightColor,
uint16_t SelectedTextColor, uint16_t SelectedColor,
uint16_t MenuColumn, uint16_t ItemRowHeight, uint16_t MaxRow,
const char *TitleText, const ILI9341_t3_font_t &ItemFont, const ILI9341_t3_font_t &TitleFont) {
itc = TextColor;
ibc = BackgroundColor;
ihtc = HighlightTextColor;
ihbc = HighlightColor;
istc = SelectedTextColor;
isbc = SelectedColor;
bcolor = TextColor;
sbcolor = TextColor;
tbt = 0; // title bar top
tbl = 0; // title bar left
tbh = 40; // title bar height
tbw = d->width(); // title bar width, default to screen width
tox = 0; // x pixel offset for title text to manage centering
toy = 0; // y pixel offset for title text to manage height centering
isx = 10; // where to start the menu item from left, default indent 10
mm = 5;
isy = tbt + tbh + mm; // where to start the menu item from top, note because array is 1 based we start drawing down a row--so back off a row, but pad 10
irh = ItemRowHeight; // select bar height
irw = tbw - isx; // select bar width, default to full width
col = MenuColumn;
imr = MaxRow; // user has to indicate this
iox = 0; // pixels to offset text in menu bar
ioy = 0; // pixels to offset text in menu bar
itemf = ItemFont; // item font
titlef = TitleFont; // title font
ttc = TextColor; // title text color
tfc = HighlightColor; // title fill color
ditc = MENU_C_DKGREY;
strncpy(ttx, TitleText, MAX_CHAR_LEN);
strncpy(etx, EXIT_TEXT, MAX_CHAR_LEN);
strncpy(dtx, EDIT_TEXT, MAX_CHAR_LEN);
currentID = 0; // id of current highlighted or selected item (1 to ID)
cr = 0; // current selected row on the screen (1 to mr-1)
totalID = 0; // maximum number of items (where first position is 1)
sr = 0; // draw offset for the menu array where first postion is 0
pr = 1; // previous selected rown (1 to mr - 1)
rowselected = false;
enablestate[0] = true;
radius = 0;
thick = 0;
}
int EditMenu::addNI(const char *ItemText, float Data, float LowLimit, float HighLimit, float Increment,
byte DecimalPlaces, const char **ItemMenuText) {
totalID++;
strcpy(itemlabel[totalID], ItemText);
data[totalID] = Data;
low[totalID] = LowLimit;
high[totalID] = HighLimit;
inc[totalID] = Increment;
dec[totalID] = DecimalPlaces;
haslist[totalID] = false;
value[totalID] = Data;
if (ItemMenuText) {
haslist[totalID] = true;
itemtext[totalID] = (char **) ItemMenuText;
}
IconType[totalID] = ICON_NONE;
enablestate[totalID] = true;
return (totalID);
}
int EditMenu::addMono(const char *ItemText, float Data, float LowLimit, float HighLimit, float Increment,
byte DecimalPlaces, const char **ItemMenuText,
const unsigned char *Bitmap, uint8_t BitmapWidth, uint8_t BitmapHeight) {
totalID++;
strcpy(itemlabel[totalID], ItemText);
data[totalID] = Data;
low[totalID] = LowLimit;
high[totalID] = HighLimit;
inc[totalID] = Increment;
dec[totalID] = DecimalPlaces;
haslist[totalID] = false;
value[totalID] = Data;
if (ItemMenuText) {
haslist[totalID] = true;
itemtext[totalID] = (char **) ItemMenuText;
}
itemBitmap[totalID] = Bitmap;
bmp_w[totalID] = BitmapWidth;
bmp_h[totalID] = BitmapHeight;
IconType[totalID] = ICON_MONO;
enablestate[totalID] = true;
return (totalID);
}
int EditMenu::add565(const char *ItemText, float Data, float LowLimit, float HighLimit, float Increment,
byte DecimalPlaces, const char **ItemMenuText,
const uint16_t *Bitmap, uint8_t BitmapWidth, uint8_t BitmapHeight) {
totalID++;
strcpy(itemlabel[totalID], ItemText);
data[totalID] = Data;
low[totalID] = LowLimit;
high[totalID] = HighLimit;
inc[totalID] = Increment;
dec[totalID] = DecimalPlaces;
haslist[totalID] = false;
value[totalID] = Data;
if (ItemMenuText) {
haslist[totalID] = true;
itemtext[totalID] = (char **) ItemMenuText;
}
item565Bitmap[totalID] = Bitmap;
bmp_w[totalID] = BitmapWidth;
bmp_h[totalID] = BitmapHeight;
IconType[totalID] = ICON_565;
enablestate[totalID] = true;
return (totalID);
}
int EditMenu::selectRow() {
InputFromTouch = false;
if (currentID == 0) {
// trigger to exit out of the menu
item = 0;
return 0;
}
// otherwise this is the trigger to enable editing in the row
rowselected = !rowselected;
drawRow(currentID);
item = currentID;
return currentID;
}
void EditMenu::drawHeader(bool ShowEdit) {
// if cr == 0 you must draw header
// if cr != 0 and was just draw, don't draw again
if (cr == 0){
dh = true;
}
else if (dh) {
dh = false;
}
else {
return;
}
d->setFont(titlef);
if (ShowEdit){
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, ihbc);
d->setTextColor(ttc);
d->print(dtx);
// up arrow
d->fillTriangle( tbl + 25 + 15 , tbt + tbh - 10, // bottom left
tbl + 25 - 15 , tbt + tbh - 10, // bottom right
tbl + 25 , tbt + 10, //center
ttc);
// down arrow
d->fillTriangle( tbw - 25 + 15 , tbt + 10, // top left
tbw - 25 - 15 , tbt + 10, // top right
tbw - 25 , tbt + tbh - 10, //center
ttc);
}
else {
if (cr == 0) {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, ihbc);
d->setTextColor(ttc);
d->print(etx);
}
else {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, tfc);
d->setTextColor(ttc);
d->print(ttx);
}
if (totalID > imr){
// up arrow
d->fillTriangle( tbl + 25 + 15 , tbt + tbh - 10, // bottom left
tbl + 25 - 15 , tbt + tbh - 10, // bottom right
tbl + 25 , tbt + 10, //center
ttc);
// down arrow
d->fillTriangle( tbw - 25 + 15 , tbt + 10, // top left
tbw - 25 - 15 , tbt + 10, // top right
tbw - 25 , tbt + tbh - 10, //center
ttc);
}
}
}
int EditMenu::press(int16_t ScreenX, int16_t ScreenY) {
int bs;
InputFromTouch = true;
// check if header pressed
if ( (ScreenX > tbl) && (ScreenX < (tbw + tbl)) && (ScreenY > tbt ) && (ScreenY < (tbt + tbh) )) {
if (ScreenX < (tbl + 25 + 15)) {
// up arrow
MoveDown();
return -1;
}
if (ScreenX > (tbw - 25 - 15)) {
// down arrow
MoveUp();
return -1;
}
// above will allow arrow up down but
// let's not allow menu exit until they are done editing the line item
if (rowselected) {
return - 1;
}
// not up / down and nothing selected, allow menu exit
return 0;
}
dh = true;
// clear the previous row (this may happen if user scroll to a row
// with a mechanical device but presses another row
drawRow(currentID);
for (i = 1; i <= imr; i++) {
bs = icox + bmp_w[i + sr] + isx;
itx = bs + iox;
if (
(ScreenX > bs) &&
(ScreenY > (isy + (irh * (i - 1)))) &&
(ScreenX < (irw)) &&
(ScreenY < (isy + irh + (irh * (i - 1))))
)
{
if (!enablestate[i + sr]) {
return -1;
}
delay(100);
if (rowselected) {
if (i == cr) {
rowselected = !rowselected;
item = i + sr;
cr = i;
currentID = i + sr;
drawHeader(false);
return currentID;
}
}
else {
rowselected = !rowselected;
currentID = i + sr;
cr = i;
drawHeader(true);
return currentID;
}
}
}
return -1;
}
void EditMenu::up() {
cr--;
currentID--;
drawItems();
if (enablestate[currentID] == false) {
while (enablestate[currentID] == false) {
cr--;
currentID--;
drawItems();
}
}
}
void EditMenu::down() {
cr++;
currentID++;
drawItems();
if (enablestate[currentID] == false) {
while (enablestate[currentID] == false) {
cr++;
currentID++;
drawItems();
}
}
}
void EditMenu::MoveUp() {
if (rowselected) {
incrementUp();
}
else {
up();
}
}
void EditMenu::MoveDown() {
if (rowselected) {
incrementDown();
}
else {
down();
}
}
void EditMenu::draw() {
dh = true;
drawItems();
}
void EditMenu::drawItems() {
int bs;
if (imr > totalID) {
imr = totalID;
}
redraw = false;
// determine if we need to pan or just increment the list
if ((currentID > totalID) && (sr >= 0) ) {
// up to top
cr = 0;
currentID = 0;
sr = 0;
redraw = true;
}
else if ( (cr < 0) && (sr == 0) ) {
// pan whole menu to bottom
cr = imr;
currentID = totalID;
sr = totalID - imr;
redraw = true;
}
else if ( (cr > imr) && ((sr + cr) > totalID) ) {
// pan whole menu to top
cr = 1;
currentID = 1;
sr = 0;
redraw = true;
}
else if ((cr > imr) && (sr >= 0)) {
// scroll whole list up one by one
sr++;
cr = imr;
redraw = true;
}
else if ((cr < 1) && (sr > 0)) {
// scroll whole list down one by one
sr--;
cr = 1;
redraw = true;
}
drawHeader(false);
d->setFont(itemf);
// now draw the items in the rows
for (i = 1; i <= imr; i++) {
// menu bar start
bs = icox + bmp_w[i + sr] + isx;
// text start
itx = bs + iox;
if (enablestate[i + sr]) {
temptColor = itc;
}
else {
temptColor = ditc;
}
if (redraw) {
// scroll so blank out every row including icon since row will get scrolled
//d->fillRect(bs, isy - irh + (irh * i), irw - bs, irh, ibc); // back color
d->fillRect(icox, isy - irh + (irh * i), irw , irh, ibc); // back color
}
if (i == pr) {
// maybe just row change so blank previous
d->fillRect(bs, isy - irh + (irh * pr) , irw - bs, irh, ibc); // back color
}
if ((i == cr) && (!InputFromTouch)) {
if (radius > 0) {
d->fillRoundRect(bs, isy - irh + (irh * i) , irw - bs, irh, radius, bcolor);
d->fillRoundRect(bs + thick, isy - irh + (irh * i) + thick, irw - bs - (2 * thick), irh - (2 * thick), radius, ihbc);
}
else {
d->fillRect(bs, isy - irh + (irh * i) , irw - bs , irh, bcolor);
d->fillRect(bs + thick , isy - irh + (irh * i) + thick, irw - bs - (2 * thick) , irh - (2 * thick), ihbc);
}
temptColor = ihtc;
}
// write bitmap
if (IconType[i + sr] == ICON_MONO) {
drawMonoBitmap(icox, icoy + isy - irh + (irh * i), itemBitmap[i + sr], bmp_w[i + sr], bmp_h[i + sr], itc );
}
else if (IconType[i + sr] == ICON_565) {
draw565Bitmap(icox, icoy + isy - irh + (irh * i), item565Bitmap[i + sr], bmp_w[i + sr], bmp_h[i + sr] );
}
// write text
d->setTextColor(temptColor);
d->setCursor(itx , isy - irh + (irh * i) + ioy);
d->print(itemlabel[i + sr]);
// write new val
d->setCursor(col , isy - irh + (irh * i) + ioy);
if (haslist[i + sr]) {
d->print(itemtext[i + sr][(int) data[i + sr]]);
}
else {
d->print(data[i + sr], dec[i + sr]);
}
}
InputFromTouch = false;
pr = cr;
}
void EditMenu::drawRow(int ID) {
int bs;
int hr = ID - sr;
uint16_t textcolor, backcolor, sbackcolor;
// compute starting place for text
itx = isx + icox + bmp_w[ID];
textcolor = itc;
if (ID <= 0) {
return;
}
bs = icox + bmp_w[hr] + isx;
if (ID <= sr) {
// item is off screen
return;
}
if (ID > (sr + imr)) {
// item is off screen
return;
}
if (InputFromTouch) {
if (ID == currentID) {
if (!rowselected) {
textcolor = itc;
backcolor = ibc;
sbackcolor = ibc;
}
else {
// case draw row is the selected row
textcolor = istc;
backcolor = isbc;
sbackcolor = sbcolor;
}
}
else {
// case draw row is not the selected row
textcolor = itc;
backcolor = ibc;
sbackcolor = ibc;
}
}
else {
if ((cr + sr) == ID) {
if (!rowselected) {
// case 1 draw som is draw row AND highlighted
textcolor = ihtc;
backcolor = ihbc;
sbackcolor = bcolor;
}
else if (rowselected) {
// case 2 = current row is draw row AND highlighted
textcolor = istc;
backcolor = isbc;
sbackcolor = ibc;
}
}
else {
// current row is not selected
if (enablestate[ID]) {
textcolor = itc;
}
else {
textcolor = ditc;
}
backcolor = ibc;
sbackcolor = ibc;
}
}
if (ID == 0) {
if (rowselected) {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, ihbc);
d->setTextColor(ttc);
d->print(ttx);
}
else {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, tfc);
d->setTextColor(ttc);
d->print(ttx);
}
}
else {
if (!InputFromTouch) {
// non touch
if (radius > 0) {
d->fillRoundRect(bs, isy - irh + (irh * hr) , irw - bs, irh, radius, sbackcolor);
d->fillRoundRect(bs + thick, isy - irh + (irh * hr) + thick, irw - bs - (2 * thick), irh - (2 * thick), radius, backcolor);
}
else {
d->fillRect(bs, isy - irh + (irh * hr) , irw - bs , irh, sbackcolor);
d->fillRect(bs + thick, isy - irh + (irh * hr) + thick, irw - bs - (2 * thick), irh - (2 * thick), backcolor);
}
}
else {
// touch
if (rowselected) {
if (radius > 0) {
d->fillRoundRect(bs, isy - irh + (irh * hr) , irw - bs, irh, radius, sbackcolor);
d->fillRoundRect(bs + thick, isy - irh + (irh * hr) + thick, irw - bs - (2 * thick), irh - (2 * thick), radius, backcolor);
}
else {
d->fillRect(bs, isy - irh + (irh * hr) , irw - bs , irh, sbackcolor);
d->fillRect(bs + thick, isy - irh + (irh * hr) + thick, irw - bs - (2 * thick), irh - (2 * thick), backcolor);
}
}
else {
d->fillRect(bs, isy - irh + (irh * hr) , irw - bs, irh, ibc); // back color
}
}
if (!enablestate[ID]) {
textcolor = ditc;
}
// write text
itx = bs + iox;
d->setFont(itemf);
d->setTextColor(textcolor);
d->setCursor(itx , isy - irh + (irh * hr) + ioy);
d->print(itemlabel[ID]);
d->setCursor(col , isy - irh + (irh * (ID - sr)) + ioy);
if (haslist[ID]) {
d->print(itemtext[ID][(int) data[ID]]);
}
else {
d->print(data[ID], dec[ID]);
}
// write bitmap
if (IconType[ID] == ICON_MONO) {
drawMonoBitmap(icox, icoy + isy - irh + (irh * (ID - sr)), itemBitmap[ID], bmp_w[ID], bmp_h[ID], temptColor );
}
else if (IconType[ID] == ICON_565) {
draw565Bitmap(icox, icoy + isy - irh + (irh * (ID - sr)), item565Bitmap[ID], bmp_w[ID], bmp_h[ID] );
}
}
}
void EditMenu::setIncrementDelay(uint16_t Delay) {
incdelay = Delay;
}
void EditMenu::incrementUp() {
d->setFont(itemf);
d->setTextColor(istc);
if (haslist[currentID]) {
if ((data[currentID] + inc[currentID]) < high[currentID]) {
data[currentID] += inc[currentID];
d->fillRect(col, isy - irh + (irh * cr) + thick, irw - col - (2 * thick), irh - (2 * thick), isbc);
d->setCursor(col , isy - irh + (irh * cr) + ioy);
d->print(itemtext[currentID][(int) data[currentID]]);
}
else {
data[currentID] = low[currentID];
d->fillRect(col, isy - irh + (irh * cr) + thick, irw - col - (2 * thick), irh - (2 * thick), isbc);
d->setCursor(col, isy - irh + (irh * cr) + ioy);
d->print(itemtext[currentID][(int) data[currentID]]);
}
}
else {
data[currentID] += inc[currentID];
if (data[currentID] > high[currentID]) {
data[currentID] = low[currentID];
}
d->fillRect(col, isy - irh + (irh * cr) + thick, irw - col - (2 * thick), irh - (2 * thick), isbc);
d->setCursor(col, isy - irh + (irh * cr) + ioy);
d->print(data[currentID], dec[currentID]);
}
if (IconType[currentID] == ICON_MONO) {
drawMonoBitmap(icox, icoy + isy - irh + (irh * cr), itemBitmap[currentID], bmp_w[currentID], bmp_h[currentID], temptColor );
}
else if (IconType[currentID] == ICON_565) {
draw565Bitmap(icox, icoy + isy - irh + (irh * cr), item565Bitmap[currentID], bmp_w[currentID], bmp_h[currentID] );
}
delay(incdelay);
value[currentID] = data[currentID];
item = currentID;
}
void EditMenu::incrementDown() {
d->setFont(itemf);
d->setTextColor(istc);
if (haslist[currentID]) {
if ((data[currentID] - inc[currentID]) >= low[currentID]) {
data[currentID] -= inc[currentID];
d->fillRect(col , isy - irh + (irh * cr) + thick, irw - col - (2 * thick), irh - (2 * thick), isbc);
d->setCursor(col , isy - irh + (irh * cr) + ioy);
d->print(itemtext[currentID][(int) data[currentID]]);
}
else {
data[currentID] = high[currentID] - 1;
d->fillRect(col , isy - irh + (irh * cr) + thick, irw - col - (2 * thick), irh - (2 * thick), isbc);
d->setCursor(col, isy - irh + (irh * cr) + ioy);
d->print(itemtext[currentID][(int) data[currentID]]);
}
}
else {
data[currentID] -= inc[currentID];
if (data[currentID] < low[currentID]) {
data[currentID] = high[currentID];
}
d->fillRect(col, isy - irh + (irh * cr) + thick, irw - col - (2 * thick), irh - (2 * thick), isbc);
d->setCursor(col , isy - irh + (irh * cr) + ioy);
d->print(data[currentID], dec[currentID]);
}
if (IconType[currentID] == ICON_MONO) {
drawMonoBitmap(icox, icoy + isy - irh + (irh * cr), itemBitmap[currentID], bmp_w[currentID], bmp_h[currentID], temptColor );
}
else if (IconType[currentID] == ICON_565) {
draw565Bitmap(icox, icoy + isy - irh + (irh * cr), item565Bitmap[currentID], bmp_w[currentID], bmp_h[currentID] );
}
delay(incdelay);
value[currentID] = data[currentID];
item = currentID;
}
void EditMenu::setTitleColors( uint16_t TitleTextColor, uint16_t TitleFillColor) {
ttc = TitleTextColor;
tfc = TitleFillColor;
}
void EditMenu::setTitleBarSize(uint16_t TitleTop, uint16_t TitleLeft, uint16_t TitleWith, uint16_t TitleHeight) {
tbt = TitleTop;
tbl = TitleLeft;
tbw = TitleWith;
tbh = TitleHeight;
}
void EditMenu::setTitleText( char *TitleText, char *ExitText) {
strncpy(ttx, TitleText, MAX_CHAR_LEN);
strncpy(etx, ExitText, MAX_CHAR_LEN);
}
void EditMenu::setTitleText( char *TitleText, char *ExitText, char *EditText) {
strncpy(ttx, TitleText, MAX_CHAR_LEN);
strncpy(dtx, EditText, MAX_CHAR_LEN);
}
void EditMenu::setTitleTextMargins(uint16_t LeftMargin, uint16_t TopMargin) {
tox = LeftMargin; // pixels to offset text in menu bar
toy = TopMargin; // pixels to offset text in menu bar
}
void EditMenu::setMenuBarMargins(uint16_t LeftMargin, uint16_t Width, uint16_t BorderRadius, uint16_t BorderThickness) {
isx = LeftMargin; // pixels to offset text in menu bar
irw = Width - isx; // pixels to offset text in menu bar
radius = BorderRadius;
thick = BorderThickness;
}
void EditMenu::setItemColors( uint16_t DisableTextColor, uint16_t HighlightBorderColor, uint16_t SelectBorderColor) {
ditc = DisableTextColor;
bcolor = HighlightBorderColor;
sbcolor = SelectBorderColor;
}
void EditMenu::setAllColors(uint16_t TextColor, uint16_t BackgroundColor,
uint16_t HighlightTextColor, uint16_t HighlightColor, uint16_t HighlightBorderColor,
uint16_t SelectedTextColor, uint16_t SelectedColor, uint16_t SelectBorderColor,
uint16_t DisableTextColor , uint16_t TitleTextColor, uint16_t TitleFillColor){
itc = TextColor;
ibc = BackgroundColor;
ihtc = HighlightTextColor;
ihbc = HighlightColor;
bcolor = HighlightBorderColor;
istc = SelectedTextColor;
isbc = SelectedColor;
sbcolor = SelectBorderColor;
ttc = TitleTextColor;
tfc = TitleFillColor;
ditc = DisableTextColor;
}
void EditMenu::setItemTextMargins(uint16_t LeftMargin, uint16_t TopMargin, uint16_t MenuMargin) {
iox = LeftMargin; // pixels to offset text in menu bar
ioy = TopMargin; // pixels to offset text in menu bar
mm = MenuMargin;
isy = tbt + tbh + mm;
}
void EditMenu::setItemText(int ID, const char *ItemText) {
strcpy(itemlabel[ID], ItemText);
drawRow(ID);
}
void EditMenu::setIconMargins(uint16_t LeftMargin, uint16_t TopMargin) {
icox = LeftMargin; // pixels to offset text in menu bar
icoy = TopMargin; // pixels to offset text in menu bar
}
void EditMenu::disable(int ID) {
enablestate[ID] = false;
}
void EditMenu::enable(int ID) {
enablestate[ID] = true;
}
bool EditMenu::getEnableState(int ID) {
return enablestate[ID];
}
void EditMenu::setItemValue(int ID, float ItemValue) {
value[ID] = ItemValue;
data[ID] = ItemValue;
}
void EditMenu::drawMonoBitmap(int16_t x, int16_t y, const unsigned char *bitmap, uint8_t w, uint8_t h, uint16_t color) {
uint8_t sbyte = 0;
uint8_t byteWidth = 0;
int jj, ii;
byteWidth = (w + 7) / 8;
for (jj = 0; jj < h; jj++) {
for (ii = 0; ii < w; ii++) {
if (ii & 7) sbyte <<= 1;
else sbyte = pgm_read_byte(bitmap + jj * byteWidth + ii / 8);
if (sbyte & 0x80) d->drawPixel(x + ii, y + jj, color);
}
}
}
void EditMenu::draw565Bitmap(int16_t x, int16_t y, const uint16_t *bitmap, uint8_t w, uint8_t h) {
uint16_t offset = 0;
int j, i;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
d->drawPixel(j + x, i + y, bitmap[offset]);
offset++;
}
}
}
/*
object type to create a simple selection only menu unlike previous where selecting a line item would allow in-line editing
*/
ItemMenu::ItemMenu(ILI9341_t3 *Display, bool EnableTouch) {
d = Display;
// we no longer use EnableTouch, but keep it in the arg list to not break
// implementations
}
void ItemMenu::init(uint16_t TextColor, uint16_t BackgroundColor,
uint16_t HighlightTextColor, uint16_t HighlightColor,
uint16_t ItemRowHeight, uint16_t MaxRow,
const char *TitleText, const ILI9341_t3_font_t &ItemFont, const ILI9341_t3_font_t &TitleFont) {
itc = TextColor;
ibc = BackgroundColor;
ihtc = HighlightTextColor;
ihbc = HighlightColor;
bcolor = TextColor;
tbt = 0; // title bar top
tbl = 0; // title bar left
tbh = 40; // title bar height
tbw = d->width(); // title bar width, default to screen width
tox = 20; // x pixel offset for title text to offset from left a bit
toy = 10; // y pixel offset for title text to manage height offset from top
isx = 0; // where to start the menu item from left, default indent 10
icox = 0;
icoy = 0;
mm = 5;
isy = tbt + tbh + mm; // where to start the menu item from top, note because array is 1 based we start drawing down a row--so back off a row, but pad 10
irh = ItemRowHeight; // select bar height
irw = tbw - isx; // select bar width, default to full width
imr = MaxRow; // user has to indicate this
iox = 0; // pixels to offset text in menu bar
ioy = 0; // pixels to offset text in menu bar
itemf = ItemFont; // item font
titlef = TitleFont; // menu bar font, default to item font
ttc = TextColor; // title text color
tfc = HighlightColor; // title fill color
ditc = MENU_C_DKGREY;
strncpy(ttx, TitleText, MAX_CHAR_LEN);
strncpy(etx, EXIT_TEXT, 6);
strncpy(dtx, EDIT_TEXT, 6);
item = 1;
drawTitleFlag = true; // flag if we draw menu bar again
currentID = 0; // id of current highlighted or selected item (1 to ID)
cr = 0; // current selected row on the screen (1 to mr-1)
totalID = 0; // maximum number of items (where first position is 1)
sr = 0; // draw offset for the menu array where first postion is 0
pr = 1; // previous selected rown (1 to mr - 1)
rowselected = false;
enablestate[0] = true;
redraw = true;
radius = 0;
thick = 0;
}
int ItemMenu::addNI(const char *ItemLabel) {
totalID++;
IconType[totalID] = ICON_NONE;
enablestate[totalID] = true;
strncpy(itemlabel[totalID], ItemLabel, MAX_CHAR_LEN);
return (totalID);
}
int ItemMenu::addMono(const char *ItemLabel, const unsigned char *Bitmap, uint8_t BitmapWidth, uint8_t BitmapHeight) {
totalID++;
itemBitmap[totalID] = Bitmap;
bmp_w[totalID] = BitmapWidth;
bmp_h[totalID] = BitmapHeight;
IconType[totalID] = ICON_MONO;
enablestate[totalID] = true;
strncpy(itemlabel[totalID], ItemLabel, MAX_CHAR_LEN);
return (totalID);
}
int ItemMenu::add565(const char *ItemLabel, const uint16_t *Bitmap, uint8_t BitmapWidth, uint8_t BitmapHeight) {
totalID++;
item565Bitmap[totalID] = Bitmap;
bmp_w[totalID] = BitmapWidth;
bmp_h[totalID] = BitmapHeight;
IconType[totalID] = ICON_565;
enablestate[totalID] = true;
strncpy(itemlabel[totalID], ItemLabel, MAX_CHAR_LEN);
return (totalID);
}
void ItemMenu::drawHeader(bool ShowEdit) {
// if cr == 0 you must draw header
// if cr != 0 and was just draw, don't draw again
if (cr == 0){
dh = true;
}
else if (dh) {
dh = false;
}
else {
return;
}
d->setFont(titlef);
if (ShowEdit){
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, ihbc);
d->setTextColor(ttc);
d->print(dtx);
// up arrow
d->fillTriangle( tbl + 25 + 15 , tbt + tbh - 10, // bottom left
tbl + 25 - 15 , tbt + tbh - 10, // bottom right
tbl + 25 , tbt + 10, //center
ttc);
// down arrow
d->fillTriangle( tbw - 25 + 15 , tbt + 10, // top left
tbw - 25 - 15 , tbt + 10, // top right
tbw - 25 , tbt + tbh - 10, //center
ttc);
}
else {
if (cr == 0) {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, ihbc);
d->setTextColor(ttc);
d->print(etx);
}
else {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, tfc);
d->setTextColor(ttc);
d->print(ttx);
}
if (totalID > imr){
// up arrow
d->fillTriangle( tbl + 25 + 15 , tbt + tbh - 10, // bottom left
tbl + 25 - 15 , tbt + tbh - 10, // bottom right
tbl + 25 , tbt + 10, //center
ttc);
// down arrow
d->fillTriangle( tbw - 25 + 15 , tbt + 10, // top left
tbw - 25 - 15 , tbt + 10, // top right
tbw - 25 , tbt + tbh - 10, //center
ttc);
}
}
}
int ItemMenu::press(int16_t ScreenX, int16_t ScreenY) {
int bs;
InputFromTouch = true;
// check if header pressed
if ( (ScreenX > tbl) && (ScreenX < (tbw + tbl)) && (ScreenY > tbt ) && (ScreenY < (tbt + tbh) )) {
if (ScreenX < (tbl + 25 + 15)) {
// up arrow
if (cr == 1) {
cr = imr;
//sr++;
}
MoveDown();
return -1;
}
if (ScreenX > (tbw - 25 - 15)) {
// down arrow
if (cr <= imr) {
cr = 0;
//currentID = sr;
}
MoveUp();
return -1;
}
return 0;
}
// clear previous row, this can happen if user scroll to a row with mechanical
// device but presses another
drawRow(currentID, BUTTON_NOTPRESSED);
// now process the menu press
for (i = 1; i <= imr; i++) {
bs = icox + bmp_w[i + sr] + isx;
itx = bs + iox;
if (
(ScreenX > bs) &&
(ScreenY > (isy + (irh * (i - 1)))) &&
(ScreenX < (irw)) &&
(ScreenY < (isy + irh + (irh * (i - 1))))
)
{
if (!enablestate[i + sr]) {
return -1;
}
cr = i + sr;
return cr;
}
}
return -1;
}
void ItemMenu::drawRow(int ID, uint8_t style) {
int bs;
int hr = ID - sr;
if (ID == 0) {
drawHeader(false);
}
if ( ID <= 0) {
return;
}
bs = icox + bmp_w[hr] + isx;
if (!enablestate[hr]) {
return;
}
if (ID == 0) {
if (style == BUTTON_PRESSED) {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, ihbc);
d->setTextColor(ttc);
d->print(ttx);
}
else {
// draw new menu bar
d->setCursor(tbl + tox, tbt + toy);
d->fillRect(tbl, tbt, tbw, tbh, tfc);
d->setTextColor(ttc);
d->print(ttx);
}
}
else {
if (style == BUTTON_PRESSED) {
if (radius > 0) {
d->fillRoundRect(bs, isy - irh + (irh * hr) , irw - bs, irh, radius, bcolor);
d->fillRoundRect(bs + thick, isy - irh + (irh * hr) + thick, irw - bs - (2 * thick), irh - (2 * thick), radius, ihbc);
}
else {
d->fillRect(bs, isy - irh + (irh * hr) , irw - bs , irh, bcolor);
d->fillRect(bs + thick , isy - irh + (irh * hr) + thick, irw - bs - (2 * thick) , irh - (2 * thick), ihbc);
}
}
else {
d->fillRect(bs, isy - irh + (irh * hr) , irw - bs, irh, ibc); // back color
}
// write text
itx = bs + iox;
d->setFont(itemf);
d->setTextColor(ihtc);
d->setCursor(itx , isy - irh + (irh * hr) + ioy);
d->print(itemlabel[ID]);
}
}
void ItemMenu::draw() {
dh = true;
drawItems();
}
void ItemMenu::drawItems() {
int bs;
if (imr > totalID) {
imr = totalID;
}
redraw = false;
// determine if we need to pan or just increment the list
if ((currentID > totalID) && (sr >= 0) ) {
// up to top
cr = 0;
currentID = 0;
sr = 0;
redraw = true;
}
else if ( (cr < 0) && (sr == 0) ) {
// pan whole menu to bottom
cr = imr;
currentID = totalID;
sr = totalID - imr;
redraw = true;
}
else if ( (cr > imr) && ((sr + cr) > totalID) ) {
// pan whole menu to top
cr = 1;
currentID = 1;
sr = 0;
redraw = true;
}
else if ((cr > imr) && (sr >= 0)) {
// scroll whole list up one by one
sr++;
cr = imr;
redraw = true;
}
else if ((cr < 1) && (sr > 0)) {
// scroll whole list down one by one
sr--;
cr = 1;
redraw = true;
}
drawHeader(false);
// determine if we need arrows
if (cr == imr) {
moredown = true;
}
if (cr == 1) {
moreup = true;
}
if (enablestate[currentID] == false) {
return;
}
d->setFont(itemf);
// now draw the items in the rows
for (i = 1; i <= imr; i++) {
// menu bar start
bs = icox + bmp_w[i + sr] + isx;
// text start
itx = bs + iox;
if (enablestate[i + sr]) {
temptColor = itc;
}
else {
temptColor = ditc;
}
if (redraw) {
d->fillRect(icox, isy - irh + (irh * i), irw , irh, ibc); // back color
}
if (i == pr) {
// maybe just row change so blank previous
d->fillRect(bs, isy - irh + (irh * pr) , irw - bs, irh, ibc); // back color
}
if ((i == cr) && (!InputFromTouch)) {
if (radius > 0) {
d->fillRoundRect(bs, isy - irh + (irh * i) , irw - bs, irh, radius, bcolor);
d->fillRoundRect(bs + thick, isy - irh + (irh * i) + thick, irw - bs - (2 * thick), irh - (2 * thick), radius, ihbc);
}
else {
d->fillRect(bs, isy - irh + (irh * i) , irw - bs , irh, bcolor);
d->fillRect(bs + thick , isy - irh + (irh * i) + thick, irw - bs - (2 * thick) , irh - (2 * thick), ihbc);
}
temptColor = ihtc;
}
// write bitmap
if (IconType[i + sr] == ICON_MONO) {
drawMonoBitmap(icox, icoy + isy - irh + (irh * i), itemBitmap[i + sr], bmp_w[i + sr], bmp_h[i + sr], itc );
}
else if (IconType[i + sr] == ICON_565) {
draw565Bitmap(icox, icoy + isy - irh + (irh * i), item565Bitmap[i + sr], bmp_w[i + sr], bmp_h[i + sr] );
}
// write text
d->setTextColor(temptColor);
d->setCursor(itx , isy - irh + (irh * i) + ioy);
d->print(itemlabel[i + sr]);
}
moreup = false;
moredown = false;
pr = cr;
InputFromTouch = false;
}
void ItemMenu::MoveUp() {
cr--;
currentID--;
drawItems();
if (enablestate[currentID] == false) {
while (enablestate[currentID] == false) {
cr--;
currentID--;
drawItems();
}
}
item = currentID;
}
void ItemMenu::MoveDown() {
cr++;
currentID++;
drawItems();
if (enablestate[currentID] == false) {
while (enablestate[currentID] == false) {
cr++;
currentID++;
drawItems();
}
}
item = currentID;
}
int ItemMenu::selectRow() {
if (currentID == 0) {
item = 0;
cr = 0;
sr = 0;
}
item = currentID;
return currentID;
}
void ItemMenu::setTitleColors( uint16_t TitleTextColor, uint16_t TitleFillColor) {
ttc = TitleTextColor;
tfc = TitleFillColor;
}
void ItemMenu::setTitleBarSize( uint16_t TitleTop, uint16_t TitleLeft, uint16_t TitleWith, uint16_t TitleHeight) {
tbt = TitleTop;
tbl = TitleLeft;
tbw = TitleWith;
tbh = TitleHeight;
}
void ItemMenu::setTitleText(char *TitleText, char *ExitText) {
strncpy(ttx, TitleText, MAX_CHAR_LEN);
strncpy(etx, ExitText, MAX_CHAR_LEN);
}
void ItemMenu::setTitleText(char *TitleText, char *ExitText, char *EditText) {
strncpy(ttx, TitleText, MAX_CHAR_LEN);
strncpy(etx, ExitText, MAX_CHAR_LEN);
strncpy(dtx, EditText, MAX_CHAR_LEN);
}
void ItemMenu::setTitleTextMargins(uint16_t LeftMargin, uint16_t TopMargin) {
tox = LeftMargin; // pixels to offset text in menu bar
toy = TopMargin; // pixels to offset text in menu bar
}
void ItemMenu::setMenuBarMargins(uint16_t LeftMargin, uint16_t Width, byte BorderRadius, byte BorderThickness) {
isx = LeftMargin; // pixels to offset text in menu bar
irw = Width - isx; // pixels to offset text in menu bar
radius = BorderRadius;
thick = BorderThickness;
}
void ItemMenu::setItemColors(uint16_t DisableTextColor, uint16_t BorderColor) {
ditc = DisableTextColor;
bcolor = BorderColor;
}
void ItemMenu::setAllColors(uint16_t TextColor, uint16_t BackgroundColor, uint16_t HighlightTextColor, uint16_t HighlightColor,
uint16_t HighLightBorderColor, uint16_t DisableTextColor, uint16_t TitleTextColor, uint16_t TitleFillColor) {
itc = TextColor;
ibc = BackgroundColor;
ihtc = HighlightTextColor;
ihbc = HighlightColor;
bcolor = HighLightBorderColor;
ttc = TitleTextColor;
tfc = TitleFillColor;
ditc = DisableTextColor;
}
void ItemMenu::setItemTextMargins(uint16_t LeftMargin, uint16_t TopMargin, uint16_t MenuMargin) {
iox = LeftMargin; // pixels to offset text in menu bar
ioy = TopMargin; // pixels to offset text in menu bar
mm = MenuMargin;
isy = tbt + tbh + mm;
}
void ItemMenu::setIconMargins(uint16_t IconOffsetX, uint16_t IconOffsetY) {
icox = IconOffsetX; // pixels to offset text in menu bar
icoy = IconOffsetY; // pixels to offset text in menu bar
}
void ItemMenu::disable(int ID) {
enablestate[ID] = false;
}
void ItemMenu::enable(int ID) {
enablestate[ID] = true;
}
bool ItemMenu::getEnableState(int ID) {
return enablestate[ID];
}
void ItemMenu::drawMonoBitmap(int16_t x, int16_t y, const unsigned char *bitmap, uint8_t w, uint8_t h, uint16_t color) {
uint8_t sbyte = 0;
uint8_t byteWidth = 0;
int jj, ii;
byteWidth = (w + 7) / 8;
for (jj = 0; jj < h; jj++) {
for (ii = 0; ii < w; ii++) {
if (ii & 7) sbyte <<= 1;
else sbyte = pgm_read_byte(bitmap + jj * byteWidth + ii / 8);
if (sbyte & 0x80) d->drawPixel(x + ii, y + jj, color);
}
}
}
void ItemMenu::draw565Bitmap(int16_t x, int16_t y, const uint16_t *bitmap, uint8_t w, uint8_t h) {
uint16_t offset = 0;
int j, i;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
d->drawPixel(j + x, i + y, bitmap[offset]);
offset++;
}
}
}
/////////////////////////////////
// end of this menu library
/////////////////////////////////