Fix drawTextRalign(): avoid wobbling when DSEG italic font is used

This commit is contained in:
Ulrich Meine
2026-08-12 23:02:49 +02:00
parent aebc18a75d
commit 50dc5955a0
3 changed files with 148 additions and 143 deletions
+22 -6
View File
@@ -572,16 +572,32 @@ void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text,
} }
// Draw right aligned text // Draw right aligned text
void drawTextRalign(int16_t x, int16_t y, String text) { // int16_t x - upper right x position for text to start printing
int16_t x1, y1; // int16_t y - upper right y position for text to start printing
uint16_t w, h; // const String& text - text to be printed
// bool dsegAdjust = false - optional, for adjustment of DSEG italic font
void drawTextRalign(int16_t x, int16_t y, const String& text, bool dsegAdjust)
{
int16_t x1 = 0, y1 = 0;
uint16_t w = 0, h = 0;
String str = text;
// make sure that we always have a char with max size at last position for boundary test
// to avoid text wobbling of DSEG italic font
if (dsegAdjust && str.length() > 0 && isDigit(str[str.length() - 1])) {
str.setCharAt(str.length() - 1, '8');
}
#ifdef TFT_DISPLAY #ifdef TFT_DISPLAY
w = getdisplay().textWidth(text); w = getdisplay().textWidth(str);
h = getdisplay().fontHeight(); h = getdisplay().fontHeight();
#else #else
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h); getdisplay().getTextBounds(str, 0, y, &x1, &y1, &w, &h);
#endif #endif
getdisplay().setCursor(x - w - 1, y); // '-1' required since some strings wrap around w/o it
int16_t cursorX = x - x1 - w;
getdisplay().setCursor(cursorX, y);
// getdisplay().setCursor(x - w - 1, y); // '-1' required since some strings wrap around w/o it
getdisplay().print(text); getdisplay().print(text);
} }
+1 -1
View File
@@ -734,7 +734,7 @@ String xdrDelete(String input); // Delete xdr prefix from string
void drawTextCenter(int16_t cx, int16_t cy, String text); void drawTextCenter(int16_t cx, int16_t cy, String text);
void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted); void drawButtonCenter(int16_t cx, int16_t cy, int8_t sx, int8_t sy, String text, uint16_t fg, uint16_t bg, bool inverted);
void drawTextRalign(int16_t x, int16_t y, String text); void drawTextRalign(int16_t x, int16_t y, const String& text, bool dsegAdjust = false);
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border); void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border);
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color);
+117 -128
View File
@@ -6,165 +6,154 @@
const int SixValues_x1 = 5; const int SixValues_x1 = 5;
const int SixValues_DeltaX = 200; const int SixValues_DeltaX = 200;
// const int SixValues_y1 = 23;
const int SixValues_y1 = 22; const int SixValues_y1 = 22;
const int SixValues_DeltaY = 83; const int SixValues_DeltaY = 83;
const int HowManyValues = 6; const int HowManyValues = 6;
class PageSixValues : public Page class PageSixValues : public Page {
{ public:
public: PageSixValues(CommonData& common)
PageSixValues(CommonData &common){ {
commonData = &common; commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSixValues"); common.logger->logDebug(GwLog::LOG, "Instantiate PageSixValues");
} }
virtual int handleKey(int key){ virtual int handleKey(int key)
{
// Code for keylock // Code for keylock
if(key == 11){ if (key == 11) {
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
int displayPage(PageData &pageData){ int displayPage(PageData& pageData)
GwConfigHandler *config = commonData->config; {
GwLog *logger = commonData->logger; GwConfigHandler* config = commonData->config;
GwLog* logger = commonData->logger;
// Old values for hold function
static String OldDataText[HowManyValues] = { "", "", "", "", "", "" };
static String OldDataUnits[HowManyValues] = { "", "", "", "", "", "" };
// Old values for hold function // Get config data
static String OldDataText[HowManyValues] = {"", "", "", "", "", ""}; String lengthformat = config->getString(config->lengthFormat);
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", ""}; bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get config data GwApi::BoatValue* bvalue;
String lengthformat = config->getString(config->lengthFormat); String DataName[HowManyValues];
// bool simulation = config->getBool(config->useSimuData); double DataValue[HowManyValues];
bool holdvalues = config->getBool(config->holdvalues); bool DataValid[HowManyValues];
String flashLED = config->getString(config->flashLED); String DataText[HowManyValues];
String backlightMode = config->getString(config->backlight); String DataUnits[HowManyValues];
String DataFormat[HowManyValues];
GwApi::BoatValue *bvalue; for (int i = 0; i < HowManyValues; i++) {
String DataName[HowManyValues]; bvalue = pageData.values[i];
double DataValue[HowManyValues]; DataName[i] = xdrDelete(bvalue->getName());
bool DataValid[HowManyValues]; DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
String DataText[HowManyValues]; DataValue[i] = bvalue->value; // Value as double in SI unit
String DataUnits[HowManyValues]; DataValid[i] = bvalue->valid;
String DataFormat[HowManyValues]; DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
DataUnits[i] = formatValue(bvalue, *commonData).unit;
DataFormat[i] = bvalue->getFormat(); // Unit of value
}
for (int i = 0; i < HowManyValues; i++){ // Optical warning by limit violation (unused)
bvalue = pageData.values[i]; if (String(flashLED) == "Limit Violation") {
DataName[i] = xdrDelete(bvalue->getName()); setBlinkingLED(false);
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name setFlashLED(false);
DataValue[i] = bvalue->value; // Value as double in SI unit }
DataValid[i] = bvalue->valid;
DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (bvalue == NULL)
DataUnits[i] = formatValue(bvalue, *commonData).unit; return PAGE_OK;
DataFormat[i] = bvalue->getFormat(); // Unit of value
// Draw page
//***********************************************************
// Set display in partial refresh mode
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
for (int i = 0; i < (HowManyValues / 2); i++) {
if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values -> standard design
// Horizontal line 3 pix
getdisplay().fillRect(0, SixValues_y1 + (i + 1) * SixValues_DeltaY, 400, 3, commonData->fgcolor);
} }
for (int j = 0; j < 2; j++) {
int ValueIndex = i * 2 + j;
int x0 = SixValues_x1 + j * SixValues_DeltaX;
int y0 = SixValues_y1 + i * SixValues_DeltaY;
LOG_DEBUG(GwLog::LOG, "Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex]);
// Optical warning by limit violation (unused) // Show name
if(String(flashLED) == "Limit Violation"){ getdisplay().setFont(&Ubuntu_Bold12pt8b);
setBlinkingLED(false); getdisplay().setCursor(x0, y0 + 25);
setFlashLED(false); getdisplay().print(DataName[ValueIndex]); // Page name
}
if (bvalue == NULL) return PAGE_OK; // WTF why this statement? // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
// Draw page if (holdvalues == false) {
//*********************************************************** drawTextRalign(x0 + 187, y0 + 19, DataUnits[ValueIndex]); // Unit
} else {
// Set display in partial refresh mode drawTextRalign(x0 + 187, y0 + 19, OldDataUnits[ValueIndex]);
displaySetPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
for (int i = 0; i < ( HowManyValues / 2 ); i++){
if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values -> standard design
// Horizontal line 3 pix
getdisplay().fillRect(0, SixValues_y1+(i+1)*SixValues_DeltaY, 400, 3, commonData->fgcolor);
} }
for (int j = 0; j < 2; j++){
int ValueIndex = i * 2 + j;
int x0 = SixValues_x1 + j * SixValues_DeltaX;
int y0 = SixValues_y1 + i * SixValues_DeltaY;
LOG_DEBUG(GwLog::LOG,"Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex] );
// Show name // Switch font depending on data type
if (DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude") {
getdisplay().setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, y0+25); } else if (DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate") {
getdisplay().print(DataName[ValueIndex]); // Page name getdisplay().setFont(&Ubuntu_Bold16pt8b);
}
// Show unit // pressure in hPa
getdisplay().setFont(&Ubuntu_Bold8pt8b); else if (DataFormat[ValueIndex] == "formatXdr:P:P") {
if(holdvalues == false){ getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
drawTextRalign(x0+187, y0+19, DataUnits[ValueIndex]); // Unit }
} // RPM
else{ else if (DataFormat[ValueIndex] == "formatXdr:T:R") {
drawTextRalign(x0+187, y0+19, OldDataUnits[ValueIndex]); getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
} }
else {
// Switch font depending on data type getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
if(DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude"){ }
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0+21, y0+60); // Show bus data
} if (holdvalues == false) {
else if(DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate"){ drawTextRalign(x0 + 187, y0 + 79, DataText[ValueIndex], true); // Real value as formatted string
getdisplay().setFont(&Ubuntu_Bold16pt8b); } else {
getdisplay().setCursor(x0+23,y0+55); drawTextRalign(x0 + 187, y0 + 79, OldDataText[ValueIndex], true); // Old value as formatted string
} }
// pressure in hPa if (DataValid[ValueIndex] == true) {
else if(DataFormat[ValueIndex] == "formatXdr:P:P"){ OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b); OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit
getdisplay().setCursor(x0+23, y0+79);
}
// RPM
else if(DataFormat[ValueIndex] == "formatXdr:T:R"){
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(x0+23, y0+79);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
if ( DataText[ValueIndex][0] == '-' )
getdisplay().setCursor(x0+23, y0+79);
else
getdisplay().setCursor(x0+65, y0+79);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(DataText[ValueIndex]); // Real value as formated string
}
else{
getdisplay().print(OldDataText[ValueIndex]); // Old value as formated string
}
if(DataValid[ValueIndex] == true){
OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit
}
} }
// Vertical line 3 pix
getdisplay().fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
} }
// Vertical line 3 pix
getdisplay().fillRect(SixValues_x1 + SixValues_DeltaX - 8, SixValues_y1 + i * SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
}
return PAGE_UPDATE; return PAGE_UPDATE;
};
}; };
static Page *createPage(CommonData &common){ };
static Page* createPage(CommonData& common)
{
return new PageSixValues(common); return new PageSixValues(common);
}/** } /**
* with the code below we make this page known to the PageTask * with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config * we give it a type (name) that can be selected in the config
* we define which function is to be called * we define which function is to be called
* and we provide the number of user parameters we expect * and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values * this will be number of BoatValue pointers in pageData.values
*/ */
PageDescription registerPageSixValues( PageDescription registerPageSixValues(
"SixValues", // Page name "SixValues", // Page name
createPage, // Action createPage, // Action
6, // Number of bus values depends on selection in Web configuration 6, // Number of bus values depends on selection in Web configuration
true // Show display header on/off true // Show display header on/off
); );
#endif #endif