TinyGPU
Loading...
Searching...
No Matches
LinePrinter.h
Go to the documentation of this file.
1#pragma once
2
3#include <string>
4
5#include "TinyGPU/Font/IFont.h"
6#include "TinyGPU/Surface/ISurface.h"
7
8namespace tinygpu {
9
10/**
11 * @brief Helper for printing wrapped lines of text onto a TinyGPU target.
12 *
13 * The printer tracks a cursor position, applies configurable borders, and
14 * wraps words to the next line when text would exceed the drawable width.
15 */
16template <typename RGB_T = RGB565>
18 public:
19 LinePrinter() = default;
20 /// Creates a line printer for the given font and target.
21 LinePrinter(IFont<RGB_T>& font, ISurface<RGB_T>& target) : font_(&font), target_(&target) {
22 currentX_ = leftBorder_;
23 currentY_ = topBorder_;
24 }
25
26 void setColor(RGB_T color) { foregroundColor_ = color; }
27 void setBackgroundColor(RGB_T color) { backgroundColor_ = color; }
28
29 /// Sets the Font implementation.
30 void setFont(IFont<RGB_T>& font) { font_ = &font; }
31 /// Sets the TinyGPU target.
32 void setTarget(ISurface<RGB_T>& target) { target_ = &target; }
33
34 /// Sets the top border in pixels.
35 void setTopBorder(size_t border) { topBorder_ = border; }
36 /// Sets the left border in pixels.
37 void setLeftBorder(size_t border) { leftBorder_ = border; }
38 /// Sets the right border in pixels.
39 void setRightBorder(size_t border) { rightBorder_ = border; }
40 /// Sets the bottom border in pixels.
41 void setButtomBorder(size_t border) { buttomBorder_ = border; }
42 /// Sets all borders to the same value.
43 void setBorders(size_t border) {
44 setTopBorder(border);
45 setLeftBorder(border);
46 setRightBorder(border);
47 setButtomBorder(border);
48 }
49
50 /// Sets the current line index.
51 void setActualLine(size_t line) {
52 currentLine_ = line;
53 currentX_ = leftBorder_;
54 currentY_ = topBorder_ + (line * lineAdvance());
55 }
56
57 /// X pixel coordinate the next character would be printed at - i.e. right
58 /// after wherever the last print()/println() call left off. Lets a caller
59 /// (e.g. a text-editor-style widget) draw a cursor at the end of the
60 /// printed text without this class needing to know anything about cursors.
61 size_t cursorX() const { return currentX_; }
62 /// Y pixel coordinate the next character would be printed at.
63 size_t cursorY() const { return currentY_; }
64
65 /// Sets the text scale factor.
66 void setScale(uint8_t scale) { this->scale = scale; }
67
68 /// Sets the spacing between glyphs.
69 void setSpacing(uint8_t spacing) { this->spacing = spacing; }
70
71 /// Enables/disables proportional (effective-width) glyph spacing on the
72 /// current font. Word-wrapping and cursor advance automatically account
73 /// for the narrower spacing since they're driven by the font's own
74 /// measureTextWidth()/drawText().
75 void setProportional(bool proportional) {
76 if (font_ != nullptr) {
77 font_->setProportional(proportional);
78 }
79 }
80 /// Returns whether proportional spacing is enabled on the current font.
81 bool isProportional() const {
82 return font_ != nullptr && font_->isProportional();
83 }
84
85 /// Prints text with word wrapping.
86 size_t print(const char* text) {
87 if (text == nullptr || *text == '\0') {
88 return 0;
89 }
90
91 ensureCursorInitialized();
92
93 std::string word;
94 size_t pendingSpaces = 0;
95 size_t printedLength = 0;
96
97 for (const char* current = text; *current != '\0'; ++current) {
98 if (*current == '\n') {
99 printWord(word, pendingSpaces);
100 pendingSpaces = 0;
101 nextLine();
102 ++printedLength;
103 continue;
104 }
105
106 if (*current == ' ') {
107 printWord(word, pendingSpaces);
108 ++pendingSpaces;
109 ++printedLength;
110 continue;
111 }
112
113 if (*current == '\t') {
114 printWord(word, pendingSpaces);
115 pendingSpaces += 4;
116 ++printedLength;
117 continue;
118 }
119
120 word.push_back(*current);
121 ++printedLength;
122 }
123
124 printWord(word, pendingSpaces);
125 flushPendingSpaces(pendingSpaces);
126 return printedLength;
127 }
128
129 /// Prints text and advances to the next line.
130 size_t println(const char* text) {
131 const size_t printedLength = print(text);
132 nextLine();
133 return printedLength;
134 }
135
136 /// Advances to the next line.
138 nextLine();
139 return 0;
140 }
141
142private:
143 IFont<RGB_T>* font_ = nullptr;
144 RGB_T backgroundColor_ = RGB_T();
145#if __cplusplus >= 201703L
146 // C++17 if constexpr for type-specific initialization
147 RGB_T foregroundColor_ = []{
148 if constexpr (std::is_same<RGB_T, bool>::value) return true;
149 else return RGB_T(255, 255, 255);
150 }();
151#else
152 RGB_T foregroundColor_ = std::is_same<RGB_T, bool>::value ? true : RGB_T(255, 255, 255);
153#endif
154 ISurface<RGB_T>* target_ = nullptr;
155 size_t topBorder_ = 20;
156 size_t leftBorder_ = 20;
157 size_t rightBorder_ = 20;
158 size_t buttomBorder_ = 20;
159 size_t currentLine_ = 0;
160 size_t currentY_ = 0;
161 size_t currentX_ = 0;
162 uint8_t scale = 1;
163 uint8_t spacing = 1;
164
165 void ensureCursorInitialized() {
166 if (currentX_ == 0 && currentY_ == 0) {
167 currentX_ = leftBorder_;
168 currentY_ = topBorder_;
169 }
170 }
171
172 size_t drawableRightEdge() const {
173 if (target_ == nullptr) {
174 return 0;
175 }
176 const size_t targetWidth = target_->width();
177 return targetWidth > rightBorder_ ? targetWidth - rightBorder_ : 0;
178 }
179
180 bool isAtLineStart() const { return currentX_ <= leftBorder_; }
181
182 size_t lineAdvance() const {
183 return font_->measureTextHeight("A", scale, 0) + spacing;
184 }
185
186 void nextLine() {
187 currentX_ = leftBorder_;
188 currentY_ += lineAdvance();
189 ++currentLine_;
190 }
191
192 void printChunk(const std::string& chunk) {
193 if (chunk.empty()) {
194 return;
195 }
196
197 target_->drawText(static_cast<int16_t>(currentX_),
198 static_cast<int16_t>(currentY_), chunk.c_str(),
199 foregroundColor_, backgroundColor_, false, scale,
200 spacing);
201 currentX_ += font_->measureTextWidth(chunk.c_str(), scale, spacing);
202 }
203
204 size_t pendingSpaceWidth(size_t pendingSpaces) const {
205 if (pendingSpaces == 0) {
206 return 0;
207 }
208
209 const std::string spaces(pendingSpaces, ' ');
210 return font_->measureTextWidth(spaces.c_str(), scale, spacing);
211 }
212
213 void flushPendingSpaces(size_t& pendingSpaces) {
214 if (pendingSpaces == 0 || isAtLineStart()) {
215 pendingSpaces = 0;
216 return;
217 }
218
219 const size_t spacesWidth = pendingSpaceWidth(pendingSpaces);
220 if ((currentX_ + spacesWidth) > drawableRightEdge()) {
221 nextLine();
222 pendingSpaces = 0;
223 return;
224 }
225
226 printChunk(std::string(pendingSpaces, ' '));
227 pendingSpaces = 0;
228 }
229
230 void printWord(std::string& word, size_t& pendingSpaces) {
231 if (word.empty()) {
232 return;
233 }
234
235 const std::string leadingSpaces =
236 isAtLineStart() ? std::string() : std::string(pendingSpaces, ' ');
237 const std::string chunk = leadingSpaces + word;
238 const size_t chunkWidth =
239 font_->measureTextWidth(chunk.c_str(), scale, spacing);
240
241 if (!isAtLineStart() && (currentX_ + chunkWidth) > drawableRightEdge()) {
242 nextLine();
243 }
244
245 if (!isAtLineStart() && pendingSpaces > 0) {
246 printChunk(leadingSpaces);
247 }
248
249 printChunk(word);
250 pendingSpaces = 0;
251 word.clear();
252 }
253};
254
255} // namespace tinygpu
Font rendering interface for TinyGPU-compatible framebuffers.
Definition: IFont.h:19
Helper for printing wrapped lines of text onto a TinyGPU target.
Definition: LinePrinter.h:17
void setBackgroundColor(RGB_T color)
Definition: LinePrinter.h:27
size_t println()
Advances to the next line.
Definition: LinePrinter.h:137
void setBorders(size_t border)
Sets all borders to the same value.
Definition: LinePrinter.h:43
void setScale(uint8_t scale)
Sets the text scale factor.
Definition: LinePrinter.h:66
void setTopBorder(size_t border)
Sets the top border in pixels.
Definition: LinePrinter.h:35
void setLeftBorder(size_t border)
Sets the left border in pixels.
Definition: LinePrinter.h:37
void setProportional(bool proportional)
Definition: LinePrinter.h:75
size_t print(const char *text)
Prints text with word wrapping.
Definition: LinePrinter.h:86
void setFont(IFont< RGB_T > &font)
Sets the Font implementation.
Definition: LinePrinter.h:30
void setSpacing(uint8_t spacing)
Sets the spacing between glyphs.
Definition: LinePrinter.h:69
void setTarget(ISurface< RGB_T > &target)
Sets the TinyGPU target.
Definition: LinePrinter.h:32
size_t cursorY() const
Y pixel coordinate the next character would be printed at.
Definition: LinePrinter.h:63
size_t cursorX() const
Definition: LinePrinter.h:61
void setRightBorder(size_t border)
Sets the right border in pixels.
Definition: LinePrinter.h:39
void setActualLine(size_t line)
Sets the current line index.
Definition: LinePrinter.h:51
void setColor(RGB_T color)
Definition: LinePrinter.h:26
LinePrinter(IFont< RGB_T > &font, ISurface< RGB_T > &target)
Creates a line printer for the given font and target.
Definition: LinePrinter.h:21
size_t println(const char *text)
Prints text and advances to the next line.
Definition: LinePrinter.h:130
void setButtomBorder(size_t border)
Sets the bottom border in pixels.
Definition: LinePrinter.h:41
bool isProportional() const
Returns whether proportional spacing is enabled on the current font.
Definition: LinePrinter.h:81
RGB color stored in 16-bit RGB565 format, byte-swapped from the conventional bit layout.
Definition: RGB565.h:24
Definition: DSIBusESP32.h:19