From e9747cf6a2595ceae81dbef5c580c5e435b4b542 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:38:54 +0000 Subject: [PATCH] Move wrap capability to lowest level print function --- esphome/components/display/display.cpp | 134 ++++++++++++++----------- esphome/components/display/display.h | 21 +++- 2 files changed, 92 insertions(+), 63 deletions(-) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 8ec7f734fa..0886f95111 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -343,6 +343,76 @@ void Display::print(int x, int y, BaseFont *font, Color color, TextAlign align, font->print(x_start, y_start, this, color, text, background); } +void Display::print(int x, int y, int width, int height, BaseFont *font, Color color, TextAlign align, const char *text, + Color background, float line_height, bool wrap) { + int x_start, y_start; + if (wrap == false) { + this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height); + font->print(x_start, y_start, this, color, text, background); + } else { + // Calculate the bounds of a single space character + int space_x1, space_y1, space_width, space_height; + this->get_text_bounds(x, y, " ", font, align, &space_x1, &space_y1, &space_width, &space_height); + + // Break text into words + std::vector words; + std::stringstream ss(text); + std::string word; + while (std::getline(ss, word, ' ')) { + words.push_back(word); + } + + // Initialize variables for the wrapped text + int line_x = x; + int line_y = y; + int x_max = x + width; + int y_max = y + height; + int word_x1, word_y1, word_width, word_height; + bool last = false; + std::string line; + + // Iterate through the words and wrap them + for (const auto &w : words) { + this->get_text_bounds(line_x, line_y, w.c_str(), font, align, &word_x1, &word_y1, &word_width, &word_height); + + if (line_x + word_width >= x_max) { + // If the next line would overspill the height box, get ready to stop. + if (line_y + 2 * static_cast(word_height * line_height) > y_max) { + last = true; + size_t length = line.length(); + const std::string cont = "..."; + if (length >= cont.length()) { + line.replace(length - cont.length(), cont.length(), cont); + } + } + // Print the current line and move to the next line + font->print(x, line_y, this, color, line.c_str(), background); + if (last) { + break; + } + line_y += static_cast(word_height * line_height); + line_x = x; + // Clear the line buffer + line.clear(); + } + + // Add the word to the line buffer and move the cursor + line += w; + line_x += word_width + space_width; + + // If it's not the last word, add a space + if (!line.empty() && &w != &words.back()) { + line += " "; + } + } + + // Print the last line + if (!line.empty()) { + font->print(x, line_y, this, color, line.c_str(), background); + } + } +} + void Display::vprintf_(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format, va_list arg) { char buffer[256]; @@ -483,79 +553,21 @@ void Display::printf(int x, int y, BaseFont *font, const char *format, ...) { va_end(arg); } -void Display::printf(int x, int y, int width, int height, BaseFont *font, float line_height, const char *format, ...) { +void Display::printf(int x, int y, int width, int height, BaseFont *font, Color color, Color background, + float line_height, const char *format, ...) { // Text wrapping printf based on https://gist.github.com/alvesvaren/767d9585f0ecbef18ef1c7c0492c4332 va_list arg; va_start(arg, format); TextAlign align = TextAlign::TOP_LEFT; - // Calculate the bounds of a single space character - int space_x1, space_y1, space_width, space_height; - this->get_text_bounds(x, y, " ", font, align, &space_x1, &space_y1, &space_width, &space_height); - char buffer[256]; int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); if (ret == 0) return; - // Break text into words - std::vector words; - std::stringstream ss(buffer); - std::string word; - while (std::getline(ss, word, ' ')) { - words.push_back(word); - } - - // Initialize variables for the wrapped text - int line_x = x; - int line_y = y; - int x_max = x + width; - int y_max = y + height; - int word_x1, word_y1, word_width, word_height; - bool last = false; - std::string line; - - // Iterate through the words and wrap them - for (const auto &w : words) { - this->get_text_bounds(line_x, line_y, w.c_str(), font, align, &word_x1, &word_y1, &word_width, &word_height); - - if (line_x + word_width >= x_max) { - // If the next line would overspill the height box, get ready to stop. - if (line_y + 2 * static_cast(word_height * line_height) > y_max) { - last = true; - size_t length = line.length(); - const std::string cont = "..."; - if (length >= cont.length()) { - line.replace(length - cont.length(), cont.length(), cont); - } - } - // Print the current line and move to the next line - this->printf(x, line_y, font, align, "%s", line.c_str()); - if (last) { - break; - } - line_y += static_cast(word_height * line_height); - line_x = x; - // Clear the line buffer - line.clear(); - } - - // Add the word to the line buffer and move the cursor - line += w; - line_x += word_width + space_width; - - // If it's not the last word, add a space - if (!line.empty() && &w != &words.back()) { - line += " "; - } - } - - // Print the last line - if (!line.empty()) { - this->printf(x, line_y, font, align, "%s", line.c_str()); - } + this->print(x, y, width, height, font, color, align, buffer, background, line_height, true); } void Display::set_writer(display_writer_t &&writer) { this->writer_ = writer; } diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 58d60343f8..84f2be7a00 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -340,6 +340,21 @@ class Display : public PollingComponent { void print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text, Color background = COLOR_OFF); + /** Print `text` with the anchor point at [x,y] with `font`. + * + * @param x The x coordinate of the text alignment anchor point. + * @param y The y coordinate of the text alignment anchor point. + * @param width The width of the bounding box to print text into. + * @param height The height of the bounding box to print text into. + * @param font The font to draw the text with. + * @param color The color to draw the text with. + * @param align The alignment of the text. + * @param text The text to draw. + * @param background When using multi-bit (anti-aliased) fonts, blend this background color into pixels + */ + void print(int x, int y, int width, int height, BaseFont *font, Color color, TextAlign align, const char *text, + Color background = COLOR_OFF, float line_height = 1.0, bool wrap = true); + /** Print `text` with the top left at [x,y] with `font`. * * @param x The x coordinate of the upper left corner. @@ -438,12 +453,14 @@ class Display : public PollingComponent { * @param width The x width of the of the box to to fill before word-wrapping. * @param height The y height of the of the box to to fill before truncating with a '...'. * @param font The font to draw the text with. + * @param color The color to draw the text with. + * @param background The background color to use for antialiasing. * @param line_height A multiplier for the line height. Set to 1.0 for standard line spacing. * @param format The format to use. * @param ... The arguments to use for the text formatting. */ - void printf(int x, int y, int width, int height, BaseFont *font, float line_height, const char *format, ...) - __attribute__((format(printf, 8, 9))); + void printf(int x, int y, int width, int height, BaseFont *font, Color color, Color background, float line_height, + const char *format, ...) __attribute__((format(printf, 10, 11))); /** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`. *