first commit
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "eslabsCurses",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Simple UI for creating interfaces with my ESP32-8048S043",
|
||||||
|
"keywords": "ESP32-8048S043, ESP32, curses",
|
||||||
|
"repository":
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ESilva15/eslabsCurses"
|
||||||
|
},
|
||||||
|
"authors":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Eduardo Silva",
|
||||||
|
"maintainer": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"Adafruit_GFX_Library": "~1.11.3",
|
||||||
|
"Adafruit_BusIO": "1.16.1"
|
||||||
|
},
|
||||||
|
"frameworks": "*",
|
||||||
|
"platforms": "*"
|
||||||
|
}
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
#include "UIBar.h"
|
||||||
|
#include "UIDrawing.h"
|
||||||
|
|
||||||
|
UIBar::UIBar(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
|
||||||
|
char *title)
|
||||||
|
: UIElement(d, dims, decor, title) {}
|
||||||
|
|
||||||
|
void UIBar::Update(char *v) {
|
||||||
|
unsigned long time = millis();
|
||||||
|
if ((time - this->lastUpdate) <= this->refreshRate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conver the string to an integer
|
||||||
|
uint32_t newVal = atoi(v);
|
||||||
|
|
||||||
|
if (this->value == newVal) {
|
||||||
|
// If the value hasn't changed we do not need to re-render
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->DrawBar(&this->value, &newVal);
|
||||||
|
this->value = newVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIBar::DrawBar(uint32_t *oldValue, uint32_t *newValue) {
|
||||||
|
// We can help identifying cur RPM visually by using different
|
||||||
|
// colors on the bar, for example, at a given distance of the ideal shift
|
||||||
|
// we can change the color gradually
|
||||||
|
|
||||||
|
// If the new value is larger than the old one
|
||||||
|
// we just paint a new rectangle
|
||||||
|
// If the new value is smaller than the old one
|
||||||
|
// we have to delete a chunk of the old rectangle
|
||||||
|
int16_t startX = 0;
|
||||||
|
int16_t len = 0;
|
||||||
|
uint16_t colour = WHITE;
|
||||||
|
|
||||||
|
if (*newValue > *oldValue) { // Increment the bar
|
||||||
|
int32_t newV =
|
||||||
|
(*newValue * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
|
||||||
|
int32_t oldV =
|
||||||
|
(this->value * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
|
||||||
|
|
||||||
|
startX = oldV;
|
||||||
|
len = newV - oldV;
|
||||||
|
|
||||||
|
} else { // Decrement the bar
|
||||||
|
int32_t newV =
|
||||||
|
(*newValue * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
|
||||||
|
int32_t oldV =
|
||||||
|
(this->value * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
|
||||||
|
|
||||||
|
startX = newV;
|
||||||
|
len = oldV - newV;
|
||||||
|
colour = BLACK;
|
||||||
|
}
|
||||||
|
this->display->fillRect(this->dims.x + 2 + startX, this->dims.y + 2, len,
|
||||||
|
this->dims.height - 4, colour);
|
||||||
|
|
||||||
|
this->value = *newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIBar::renderBlank() {
|
||||||
|
this->display->setCursor(this->dims.x, this->dims.y);
|
||||||
|
this->display->print("!RANGE");
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIBar::Box() {
|
||||||
|
// If the range was not set, we cannot draw our ruler
|
||||||
|
if (this->range <= 0) {
|
||||||
|
this->renderBlank();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bounding box for the bar
|
||||||
|
this->display->drawRect(this->dims.x, this->dims.y, this->dims.width,
|
||||||
|
this->dims.height, RED);
|
||||||
|
|
||||||
|
// Now we can draw the scale (this should be easy to modify if needed)
|
||||||
|
// dividirs will be the range of the tach - 8 = 8000rpm
|
||||||
|
|
||||||
|
// Calculate step size as a float for accurate positioning
|
||||||
|
float step = (float)this->dims.width / this->range;
|
||||||
|
for (int k = 0; k <= this->range; k++) {
|
||||||
|
uint16_t x = round(k * step) + this->dims.x;
|
||||||
|
|
||||||
|
// To ensure it doesn't go beyond the bounding box
|
||||||
|
if (x >= this->dims.x + this->dims.width) {
|
||||||
|
x = this->dims.x + this->dims.width - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->display->setTextColor(RED);
|
||||||
|
this->display->drawFastVLine(x, this->dims.y + this->dims.height, 7, RED);
|
||||||
|
|
||||||
|
char legend[5];
|
||||||
|
sprintf(legend, "%d", k);
|
||||||
|
this->display->setCursor(x - (CHR_WIDTH(this->decor->textSize) / 2),
|
||||||
|
this->dims.y + this->dims.height + 7 + 2);
|
||||||
|
this->display->setTextColor(WHITE);
|
||||||
|
this->display->setTextSize(this->decor->textSize);
|
||||||
|
this->display->print(legend);
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef _UI_BAR
|
||||||
|
#define _UI_BAR
|
||||||
|
|
||||||
|
#include "UIComponent.h"
|
||||||
|
|
||||||
|
struct UIBar : UIElement {
|
||||||
|
// This might be the only when where its more efficient to have the data
|
||||||
|
// as integers instead
|
||||||
|
uint32_t value = 0; // current tach value
|
||||||
|
uint8_t range = 0; // define the tach range, ie: 8 for 8000rpm
|
||||||
|
|
||||||
|
UIBar(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor, char *title);
|
||||||
|
|
||||||
|
void Update(char *val);
|
||||||
|
void DrawBar(uint32_t *oldValue, uint32_t *newValue);
|
||||||
|
void renderBlank();
|
||||||
|
void Box();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#include "UIComponent.h"
|
||||||
|
// #include "u8g2.h"
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
#include "UIDrawing.h"
|
||||||
|
// #include <U8g2lib.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define DEBUG 1
|
||||||
|
|
||||||
|
UIElement::UIElement(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
|
||||||
|
char *title)
|
||||||
|
: display(d), dims(dims), decor(decor), title(title) {}
|
||||||
|
|
||||||
|
UIDimensions::UIDimensions(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
|
||||||
|
: x(x), y(y), width(w), height(h) {}
|
||||||
|
|
||||||
|
int16_t UIElement::getContentAreaX0() {
|
||||||
|
if (this->decor->hasBorder) {
|
||||||
|
return this->dims.x + DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->dims.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t UIElement::getContentAreaY0() {
|
||||||
|
if (this->decor->hasBorder) {
|
||||||
|
return this->dims.y + this->getTitleAreaHeight() +
|
||||||
|
DEFAULT_TITLE_CONTENT_SPACING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->dims.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t UIElement::getContentAreaHeight() {
|
||||||
|
return this->dims.height - (DEFAULT_MARGIN * 2) - DEFAULT_BORDER_THICKNESS -
|
||||||
|
this->getTitleAreaHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t UIElement::getContentAreaWidth() {
|
||||||
|
return this->dims.width - ((DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t UIElement::getTitleAreaX0() {
|
||||||
|
return this->dims.x + DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t UIElement::getTitleAreaY0() { return this->dims.y; }
|
||||||
|
|
||||||
|
uint16_t UIElement::getTitleAreaHeight() {
|
||||||
|
int16_t x = 0, y = 0;
|
||||||
|
uint16_t w = 0, h = 0;
|
||||||
|
|
||||||
|
this->display->setTextSize(this->decor->titleSize);
|
||||||
|
this->display->getTextBounds((const char *)this->title, 0, 0, &x, &y, &w, &h);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t UIElement::getTitleAreaWidth() {
|
||||||
|
int16_t x = 0, y = 0;
|
||||||
|
uint16_t w = 0, h = 0;
|
||||||
|
|
||||||
|
this->display->setTextSize(this->decor->titleSize);
|
||||||
|
this->display->getTextBounds((const char *)this->title, 0, 0, &x, &y, &w, &h);
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIElement::replaceString(char *oldVal, char *newVal) {
|
||||||
|
size_t newValLen = strlen(newVal);
|
||||||
|
size_t oldValLen = strlen(oldVal);
|
||||||
|
|
||||||
|
// It crashs somewhere around here
|
||||||
|
int16_t x0 = this->getContentAreaX0();
|
||||||
|
int16_t y0 = this->getContentAreaY0(); // <- Crashes here
|
||||||
|
// Before here
|
||||||
|
|
||||||
|
this->display->setTextSize(this->decor->textSize);
|
||||||
|
|
||||||
|
// If the new value is shorter, delete the extra in advance
|
||||||
|
if (oldValLen > newValLen) {
|
||||||
|
// If the old value is longer than the new value, we have to delete the
|
||||||
|
// extra of the old value, which is whatever starts at the new value len
|
||||||
|
for (size_t start = newValLen; start < oldValLen; start++) {
|
||||||
|
this->display->setCursor(x0 + start * (CHR_WIDTH(this->decor->textSize)),
|
||||||
|
y0);
|
||||||
|
this->display->setTextColor(this->decor->bgColor);
|
||||||
|
this->display->print(oldVal[start]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t k = 0; k < newValLen; k++) {
|
||||||
|
if (k <= oldValLen) {
|
||||||
|
if (oldVal[k] != newVal[k]) {
|
||||||
|
this->display->setCursor(x0 + k * (CHR_WIDTH(this->decor->textSize)),
|
||||||
|
y0 + 0);
|
||||||
|
this->display->setTextColor(this->decor->bgColor);
|
||||||
|
this->display->print(oldVal[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->display->setCursor(x0 + k * (CHR_WIDTH(this->decor->textSize)),
|
||||||
|
y0 + 0);
|
||||||
|
this->display->setTextColor(this->decor->fgColor);
|
||||||
|
this->display->print(newVal[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#ifndef __UI_COMPONENT
|
||||||
|
#define __UI_COMPONENT
|
||||||
|
|
||||||
|
#include "Arduino_GFX.h"
|
||||||
|
#include "UIDecorations.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum ComponentType {
|
||||||
|
STRING,
|
||||||
|
TABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UIDimensions {
|
||||||
|
uint16_t x = 0, y = 0;
|
||||||
|
uint16_t width = 0, height = 0;
|
||||||
|
|
||||||
|
UIDimensions(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UIElement {
|
||||||
|
Arduino_GFX *display;
|
||||||
|
UIDimensions dims;
|
||||||
|
UIDecorations *decor;
|
||||||
|
char *title;
|
||||||
|
uint64_t lastUpdate = 0;
|
||||||
|
uint16_t refreshRate = 0;
|
||||||
|
ComponentType type;
|
||||||
|
|
||||||
|
UIElement(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
|
||||||
|
char *Title);
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
int16_t getContentAreaX0();
|
||||||
|
int16_t getContentAreaY0();
|
||||||
|
uint16_t getContentAreaHeight();
|
||||||
|
uint16_t getContentAreaWidth();
|
||||||
|
int16_t getTitleAreaX0();
|
||||||
|
int16_t getTitleAreaY0();
|
||||||
|
uint16_t getTitleAreaHeight();
|
||||||
|
uint16_t getTitleAreaWidth();
|
||||||
|
|
||||||
|
// Positioning
|
||||||
|
void horizontalCenter(UIElement *reference);
|
||||||
|
void verticalCenter(UIElement *reference);
|
||||||
|
void placeBelow(UIElement *reference);
|
||||||
|
void placeRight(UIElement *reference);
|
||||||
|
void placeLeft(UIElement *reference);
|
||||||
|
|
||||||
|
// Drawing
|
||||||
|
void drawBox();
|
||||||
|
void noBox();
|
||||||
|
void replaceString(char *oldVal, char *newVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include "UIDecorations.h"
|
||||||
|
|
||||||
|
UIDecorations::UIDecorations() {}
|
||||||
|
UIDecorations::UIDecorations(uint16_t bg, uint16_t fg, uint16_t title,
|
||||||
|
uint16_t border, uint8_t titleSize,
|
||||||
|
uint8_t textSize) {}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef __UI_DECORATIONS
|
||||||
|
#define __UI_DECORATIONS
|
||||||
|
|
||||||
|
#include "UIDrawing.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct UIDecorations {
|
||||||
|
bool hasBorder = true;
|
||||||
|
uint16_t bgColor = MAIN_BG_COLOR, fgColor = MAIN_FG_COLOR;
|
||||||
|
uint16_t titleColor = MAIN_FG_COLOR, borderColor = RED;
|
||||||
|
uint8_t titleSize = 2, textSize = 4;
|
||||||
|
|
||||||
|
UIDecorations();
|
||||||
|
UIDecorations(uint16_t bg, uint16_t fg, uint16_t title, uint16_t border,
|
||||||
|
uint8_t titleSize, uint8_t textSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
#include "UIDrawing.h"
|
||||||
|
#include "Arduino_GFX.h"
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
#include "UIComponent.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
* On the placement functions, change 5 to a variable or something
|
||||||
|
*/
|
||||||
|
|
||||||
|
// #define DEBUG
|
||||||
|
|
||||||
|
uint16_t calculateHeight(int16_t titleSize, int16_t textSize, int16_t nLines) {
|
||||||
|
return CHR_HEIGHT(titleSize) + CHR_HEIGHT(textSize) +
|
||||||
|
DEFAULT_TITLE_CONTENT_SPACING +
|
||||||
|
(2 * (DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t calculateWidth(int16_t textSize, int16_t nChars) {
|
||||||
|
return CHR_WIDTH(textSize) * nChars +
|
||||||
|
(2 * (DEFAULT_MARGIN + DEFAULT_BORDER_THICKNESS));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIElement::horizontalCenter(UIElement *ref) {
|
||||||
|
uint16_t middlePoint = 0;
|
||||||
|
|
||||||
|
if (ref != nullptr) {
|
||||||
|
Serial2.println("We are here indeed!");
|
||||||
|
middlePoint = ref->dims.x + (ref->dims.width / 2);
|
||||||
|
} else {
|
||||||
|
middlePoint = this->display->width() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->dims.x = middlePoint - (this->dims.width / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can add a mode of alignment here, center, left, right, whatever
|
||||||
|
void UIElement::placeBelow(UIElement *ref) {
|
||||||
|
// For now the default alignment will be left until I need some other
|
||||||
|
this->dims.x = ref->dims.x;
|
||||||
|
this->dims.y = ref->dims.y + ref->dims.height + 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIElement::placeRight(UIElement *ref) {
|
||||||
|
this->dims.x = ref->dims.x + ref->dims.width + 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIElement::placeLeft(UIElement *ref) {
|
||||||
|
this->dims.x = ref->dims.x - this->dims.width - 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIElement::drawBox() {
|
||||||
|
// This is the border rectangle
|
||||||
|
if (this->decor->hasBorder) {
|
||||||
|
for (int k = 0; k < DEFAULT_BORDER_THICKNESS; k++) {
|
||||||
|
this->display->drawRect(
|
||||||
|
this->dims.x + k, this->dims.y + k, this->dims.width - (k * 2),
|
||||||
|
this->dims.height - (k * 2), this->decor->borderColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging purpouses
|
||||||
|
// title area
|
||||||
|
int16_t x = 0, y = 0;
|
||||||
|
uint16_t w = 0, h = 0;
|
||||||
|
|
||||||
|
// U8G2 u8g2; // No display pin setup needed since we're only using font data
|
||||||
|
// u8g2.setFont(u8g2_font_9x18_tf);
|
||||||
|
// int ascent = u8g2.getAscent();
|
||||||
|
|
||||||
|
h = this->getTitleAreaHeight();
|
||||||
|
uint16_t titleAreaWidth = this->getTitleAreaWidth();
|
||||||
|
int16_t titleTopLeftX = this->getTitleAreaX0();
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Title area bounds
|
||||||
|
this->display->drawRect(titleTopLeftX, this->getTitleAreaY0(), titleAreaWidth,
|
||||||
|
h, GREEN);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// content area
|
||||||
|
x = this->getContentAreaX0();
|
||||||
|
y = this->getContentAreaY0();
|
||||||
|
w = this->getContentAreaWidth();
|
||||||
|
h = this->getContentAreaHeight();
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Content area bounds
|
||||||
|
this->display->drawRect(x, y, w, h, BLUE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (this->decor->hasBorder) {
|
||||||
|
// Remove the border line behind the title
|
||||||
|
this->display->fillRect(titleTopLeftX - DEFAULT_MARGIN, this->dims.y,
|
||||||
|
titleAreaWidth + DEFAULT_MARGIN,
|
||||||
|
DEFAULT_BORDER_THICKNESS, MAIN_BG_COLOR);
|
||||||
|
|
||||||
|
// Render the title
|
||||||
|
this->display->setCursor(titleTopLeftX, this->dims.y);
|
||||||
|
this->display->print(this->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Element bounds
|
||||||
|
this->display->drawRect(this->dims.x, this->dims.y, this->dims.width,
|
||||||
|
this->dims.height, PURPLE);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef __CURSES_UI_VISUALS
|
||||||
|
#define __CURSES_UI_VISUALS
|
||||||
|
|
||||||
|
#include <Arduino_GFX.h>
|
||||||
|
|
||||||
|
#define RIGHT 0
|
||||||
|
#define INVERTED 1
|
||||||
|
#define LEFT 2
|
||||||
|
#define NORMAL 3
|
||||||
|
|
||||||
|
#define DEFAULT_BORDER_THICKNESS 3
|
||||||
|
#define DEFAULT_TITLE_CONTENT_SPACING 3
|
||||||
|
#define DEFAULT_MARGIN 3
|
||||||
|
|
||||||
|
#define MAIN_BG_COLOR RGB565(20, 10, 10)
|
||||||
|
#define MAIN_FG_COLOR RGB565(255, 255, 255)
|
||||||
|
|
||||||
|
#define CHR_WIDTH(size) (6 * size)
|
||||||
|
#define CHR_HEIGHT(size) (8 * size)
|
||||||
|
#define CHR_SPACE(size) (size)
|
||||||
|
|
||||||
|
uint16_t calculateHeight(int16_t titleSize, int16_t textSize, int16_t nLines);
|
||||||
|
uint16_t calculateWidth(int16_t textSize, int16_t nChars);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include "UIString.h"
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
#include "UIComponent.h"
|
||||||
|
#include <Arduino_GFX.h>
|
||||||
|
|
||||||
|
UIString::UIString() : UIElement(nullptr, {0, 0, 0, 0}, nullptr, (char *)"") {
|
||||||
|
this->type = STRING;
|
||||||
|
memset(this->value, 0, this->bufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
UIString::UIString(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
|
||||||
|
char *title)
|
||||||
|
: UIElement(d, dims, decor, title) {
|
||||||
|
this->type = STRING;
|
||||||
|
memset(this->value, 0, this->bufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIString::Update(const char *v) {
|
||||||
|
uint64_t time = millis();
|
||||||
|
if ((time - this->lastUpdate) <= this->refreshRate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value is larger than our buffer - should add a way of knowing we
|
||||||
|
// got an error here
|
||||||
|
if (strlen(v) >= this->bufferSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the value hasn't changed we do not need to re-render
|
||||||
|
if (strcmp(this->value, v) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char newVal[this->bufferSize];
|
||||||
|
memset(newVal, 0, this->bufferSize);
|
||||||
|
// strncpy(newVal, v, this->bufferSize - 1);
|
||||||
|
sprintf(newVal, "%s", v);
|
||||||
|
|
||||||
|
char oldVal[this->bufferSize];
|
||||||
|
memset(oldVal, 0, this->bufferSize);
|
||||||
|
// strncpy(oldVal, v, this->bufferSize - 1);
|
||||||
|
sprintf(oldVal, "%s", this->value);
|
||||||
|
|
||||||
|
// Figure out the x0 and y0 for the text
|
||||||
|
replaceString(oldVal, newVal);
|
||||||
|
strncpy(this->value, v, this->bufferSize);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef __UI_STRING
|
||||||
|
#define __UI_STRING
|
||||||
|
|
||||||
|
#include "UIComponent.h"
|
||||||
|
|
||||||
|
// Representation of single line strings
|
||||||
|
struct UIString : UIElement {
|
||||||
|
static const size_t bufferSize = 64;
|
||||||
|
char value[bufferSize];
|
||||||
|
|
||||||
|
UIString();
|
||||||
|
UIString(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
|
||||||
|
char *title);
|
||||||
|
void Update(const char *value);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#include "UITable.h"
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
#include "UIDecorations.h"
|
||||||
|
#include "UIDrawing.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define DEBUG
|
||||||
|
|
||||||
|
UITable::UITable(Arduino_GFX *gfx, UIDimensions dims, UIDecorations *decor,
|
||||||
|
int nRows, int nCols, int *colWidths, char *title)
|
||||||
|
: UIElement(gfx, dims, decor, title) {
|
||||||
|
this->type = TABLE;
|
||||||
|
this->nRows = nRows;
|
||||||
|
this->nColumns = nCols;
|
||||||
|
this->tableData = new UIString *[this->nRows * this->nColumns];
|
||||||
|
this->colWidths = colWidths;
|
||||||
|
|
||||||
|
// Default decoration for every cell
|
||||||
|
this->decor->textSize = 2;
|
||||||
|
|
||||||
|
for (int k = 0; k < ROWS * COLUMNS; k++) {
|
||||||
|
UIDecorations *cellDecor = new UIDecorations();
|
||||||
|
cellDecor->textSize = this->decor->textSize;
|
||||||
|
|
||||||
|
UIDimensions dims = {0, 0, 0, 0};
|
||||||
|
|
||||||
|
this->tableData[k] = new UIString(display, dims, cellDecor, (char *)"");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cellH = CHR_HEIGHT(this->decor->textSize) + CELL_MARGIN;
|
||||||
|
|
||||||
|
// Initialize the table dimensions
|
||||||
|
//// Calculate the height
|
||||||
|
this->dims.height = ROWS * cellH + this->getTitleAreaHeight() +
|
||||||
|
DEFAULT_MARGIN + DEFAULT_BORDER_THICKNESS;
|
||||||
|
|
||||||
|
//// Calculate the width
|
||||||
|
for (int c = 0; c < this->nColumns; c++) {
|
||||||
|
this->dims.width +=
|
||||||
|
(CHR_WIDTH(this->decor->textSize) + CELL_MARGIN) * this->colWidths[c];
|
||||||
|
}
|
||||||
|
this->dims.width += (DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN) * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t UITable::getContentAreaHeight() {
|
||||||
|
return this->dims.height - (DEFAULT_MARGIN * 2) - DEFAULT_BORDER_THICKNESS -
|
||||||
|
this->getTitleAreaHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This was hardcoded for the relative and had a warning for that but
|
||||||
|
* I recon its dynamic enough for now
|
||||||
|
*/
|
||||||
|
void UITable::setup() {
|
||||||
|
int cellH = CHR_HEIGHT(this->decor->textSize) + CELL_MARGIN;
|
||||||
|
|
||||||
|
// Initialize the cells
|
||||||
|
uint16_t yOffset = this->getContentAreaY0();
|
||||||
|
for (int r = 0; r < ROWS; r++) {
|
||||||
|
int16_t xOffset = this->getContentAreaX0();
|
||||||
|
for (int c = 0; c < COLUMNS; c++) {
|
||||||
|
this->tableData[r * COLUMNS + c]->refreshRate = this->refreshRate;
|
||||||
|
this->tableData[r * COLUMNS + c]->decor->textSize = this->decor->textSize;
|
||||||
|
this->tableData[r * COLUMNS + c]->decor->hasBorder = false;
|
||||||
|
this->tableData[r * COLUMNS + c]->dims.x = xOffset + CELL_MARGIN;
|
||||||
|
this->tableData[r * COLUMNS + c]->dims.y = (cellH * r) + yOffset;
|
||||||
|
this->tableData[r * COLUMNS + c]->dims.height = cellH;
|
||||||
|
this->tableData[r * COLUMNS + c]->dims.width =
|
||||||
|
this->colWidths[c] * (CHR_WIDTH(this->decor->textSize) + CELL_MARGIN);
|
||||||
|
memset(this->tableData[r * COLUMNS + c]->value, 0,
|
||||||
|
this->tableData[r * COLUMNS + c]->bufferSize);
|
||||||
|
|
||||||
|
xOffset +=
|
||||||
|
this->colWidths[c] * (CHR_WIDTH(this->decor->textSize) + CELL_MARGIN);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
this->tableData[r * COLUMNS + c]->drawBox();
|
||||||
|
this->tableData[r * COLUMNS + c]->Update("---");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef __UI_TABLE
|
||||||
|
#define __UI_TABLE
|
||||||
|
|
||||||
|
#include "Arduino_GFX.h"
|
||||||
|
#include "UIComponent.h"
|
||||||
|
#include "UIString.h"
|
||||||
|
|
||||||
|
#define COLUMNS 3
|
||||||
|
#define ROWS 5
|
||||||
|
#define CELL_MARGIN 2
|
||||||
|
|
||||||
|
/* Very shitty table element, only works for the relative as of now
|
||||||
|
*/
|
||||||
|
struct UITable : UIElement {
|
||||||
|
// will hold rows * columns UIStrings to fill the table
|
||||||
|
int nRows = 0;
|
||||||
|
int nColumns = 0;
|
||||||
|
int *colWidths;
|
||||||
|
UIString **tableData;
|
||||||
|
|
||||||
|
UITable(Arduino_GFX *gfx, UIDimensions dims, UIDecorations *decor, int nR,
|
||||||
|
int nC, int *cWidth, char *t);
|
||||||
|
|
||||||
|
// void Update(StandingLine standings[5]);
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
uint16_t getContentAreaHeight();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user