mirror of
https://github.com/Architeuthis-Flux/JumperlessV5.git
synced 2025-09-04 02:27:57 +00:00
315 lines
7.8 KiB
Markdown
315 lines
7.8 KiB
Markdown
# Font Bounds System for Jumperless OLED
|
|
|
|
This system provides comprehensive font measurement and text positioning capabilities for the 128x32 pixel OLED display on the Jumperless V5.
|
|
|
|
## Features
|
|
|
|
✅ **Pixel-perfect text positioning**
|
|
✅ **Automatic text centering and alignment**
|
|
✅ **Text fit validation**
|
|
✅ **Smart word wrapping**
|
|
✅ **Multi-font support**
|
|
✅ **Character-level analysis**
|
|
✅ **Font metrics calculation**
|
|
|
|
## Quick Start
|
|
|
|
1. Include the header in your firmware:
|
|
```cpp
|
|
#include "examples/font_bounds_example.h"
|
|
```
|
|
|
|
2. Basic usage:
|
|
```cpp
|
|
// Check if text fits
|
|
if (oled.textFits("Hello World")) {
|
|
// Center the text
|
|
int16_t x, y;
|
|
oled.getCenteredPosition("Hello World", &x, &y);
|
|
oled.setCursor(x, y);
|
|
oled.print("Hello World");
|
|
oled.show();
|
|
}
|
|
```
|
|
|
|
## Core Functions
|
|
|
|
### Text Measurement
|
|
|
|
#### `getTextBounds(const char* str)`
|
|
Returns detailed measurements for any text string.
|
|
|
|
```cpp
|
|
TextBounds bounds = oled.getTextBounds("Sample Text");
|
|
// bounds.width, bounds.height, bounds.x1, bounds.y1, bounds.x2, bounds.y2
|
|
// bounds.baseline, bounds.ascent, bounds.descent
|
|
```
|
|
|
|
#### `getCharBounds(char c)`
|
|
Get metrics for individual characters.
|
|
|
|
```cpp
|
|
CharBounds bounds = oled.getCharBounds('A');
|
|
// bounds.width, bounds.height, bounds.xAdvance, bounds.xOffset, bounds.yOffset
|
|
```
|
|
|
|
### Text Positioning
|
|
|
|
#### `getCenteredPosition(const char* str, int16_t* x, int16_t* y)`
|
|
Calculate perfect center position for text.
|
|
|
|
```cpp
|
|
int16_t x, y;
|
|
oled.getCenteredPosition("Centered Text", &x, &y);
|
|
oled.setCursor(x, y);
|
|
oled.print("Centered Text");
|
|
```
|
|
|
|
#### `getCenteredX(const char* str, int16_t* x)`
|
|
Center horizontally only.
|
|
|
|
```cpp
|
|
int16_t x;
|
|
oled.getCenteredX("Horizontal Center", &x);
|
|
oled.setCursor(x, 16); // Your Y position
|
|
oled.print("Horizontal Center");
|
|
```
|
|
|
|
### Text Fitting
|
|
|
|
#### `textFits(const char* str)`
|
|
Check if text fits anywhere on display.
|
|
|
|
```cpp
|
|
if (oled.textFits("Long text message")) {
|
|
// Display normally
|
|
} else {
|
|
// Use word wrapping or smaller font
|
|
}
|
|
```
|
|
|
|
#### `textFitsAt(const char* str, int16_t x, int16_t y)`
|
|
Check if text fits at specific position.
|
|
|
|
```cpp
|
|
if (oled.textFitsAt("Status", 0, 24)) {
|
|
oled.setCursor(0, 24);
|
|
oled.print("Status");
|
|
}
|
|
```
|
|
|
|
### Word Wrapping
|
|
|
|
#### `wrapText(const char* text)`
|
|
Automatically break long text into lines.
|
|
|
|
```cpp
|
|
std::vector<String> lines = oled.wrapText("Very long text that needs to be wrapped");
|
|
int lineHeight = oled.getFontHeight(oled.currentFont);
|
|
|
|
for (int i = 0; i < lines.size(); i++) {
|
|
oled.setCursor(0, i * lineHeight);
|
|
oled.print(lines[i]);
|
|
}
|
|
```
|
|
|
|
### Font Analysis
|
|
|
|
#### `getFontMetrics()`
|
|
Get comprehensive font information.
|
|
|
|
```cpp
|
|
FontMetrics metrics = oled.getFontMetrics();
|
|
// metrics.lineHeight, metrics.ascent, metrics.descent
|
|
// metrics.maxWidth, metrics.avgWidth, metrics.spaceWidth
|
|
```
|
|
|
|
#### `getMaxLines()` and `getCharsPerLine()`
|
|
Calculate layout constraints.
|
|
|
|
```cpp
|
|
int maxLines = oled.getMaxLines(); // How many lines fit
|
|
int charsPerLine = oled.getCharsPerLine(); // Approximate chars per line
|
|
```
|
|
|
|
## Data Structures
|
|
|
|
### TextBounds
|
|
```cpp
|
|
struct TextBounds {
|
|
int16_t width, height; // Total dimensions
|
|
int16_t x1, y1, x2, y2; // Bounding box corners
|
|
int16_t baseline; // Baseline position
|
|
int16_t ascent, descent; // Font metrics
|
|
};
|
|
```
|
|
|
|
### CharBounds
|
|
```cpp
|
|
struct CharBounds {
|
|
int16_t width, height; // Character dimensions
|
|
int16_t xAdvance; // Cursor movement
|
|
int16_t xOffset, yOffset; // Position offsets
|
|
int16_t x1, y1, x2, y2; // Bounding box
|
|
};
|
|
```
|
|
|
|
### FontMetrics
|
|
```cpp
|
|
struct FontMetrics {
|
|
int16_t lineHeight; // Line spacing
|
|
int16_t ascent; // Max height above baseline
|
|
int16_t descent; // Max depth below baseline
|
|
int16_t maxWidth; // Widest character
|
|
int16_t avgWidth; // Average character width
|
|
int16_t spaceWidth; // Space character width
|
|
};
|
|
```
|
|
|
|
## Practical Examples
|
|
|
|
### Smart Status Display
|
|
```cpp
|
|
void displayStatus(const char* title, const char* status, int value) {
|
|
oled.clear();
|
|
|
|
// Centered title at top
|
|
int16_t x, y;
|
|
oled.getCenteredX(title, &x);
|
|
TextBounds titleBounds = oled.getTextBounds(title);
|
|
oled.setCursor(x, titleBounds.ascent + 2);
|
|
oled.print(title);
|
|
|
|
// Left-aligned status
|
|
oled.setCursor(0, 16);
|
|
oled.print(status);
|
|
|
|
// Right-aligned value
|
|
String valueStr = String(value);
|
|
TextBounds valueBounds = oled.getTextBounds(valueStr.c_str());
|
|
oled.setCursor(SCREEN_WIDTH - valueBounds.width, 16);
|
|
oled.print(valueStr);
|
|
|
|
oled.show();
|
|
}
|
|
```
|
|
|
|
### Adaptive Font Selection
|
|
```cpp
|
|
void smartDisplay(const char* message) {
|
|
// Try fonts from largest to smallest
|
|
bool displayed = false;
|
|
|
|
for (int fontIndex = numFonts - 1; fontIndex >= 0 && !displayed; fontIndex--) {
|
|
oled.setFont(fontIndex);
|
|
|
|
if (oled.textFits(message)) {
|
|
int16_t x, y;
|
|
oled.getCenteredPosition(message, &x, &y);
|
|
oled.setCursor(x, y);
|
|
oled.print(message);
|
|
displayed = true;
|
|
}
|
|
}
|
|
|
|
if (!displayed) {
|
|
// Use word wrapping with smallest font
|
|
oled.setFont(0);
|
|
std::vector<String> lines = oled.wrapText(message);
|
|
// ... display wrapped lines
|
|
}
|
|
}
|
|
```
|
|
|
|
### Menu with Proper Spacing
|
|
```cpp
|
|
void displayMenu(const char* items[], int count, int selected) {
|
|
oled.clear();
|
|
|
|
FontMetrics metrics = oled.getFontMetrics();
|
|
int lineHeight = metrics.lineHeight;
|
|
int maxItems = SCREEN_HEIGHT / lineHeight;
|
|
|
|
for (int i = 0; i < count && i < maxItems; i++) {
|
|
int y = i * lineHeight;
|
|
|
|
// Highlight selected item
|
|
if (i == selected) {
|
|
oled.setCursor(0, y);
|
|
oled.print(">");
|
|
}
|
|
|
|
oled.setCursor(8, y);
|
|
oled.print(items[i]);
|
|
}
|
|
|
|
oled.show();
|
|
}
|
|
```
|
|
|
|
## Integration into Firmware
|
|
|
|
### Option 1: Direct Integration
|
|
Add to your main firmware files:
|
|
```cpp
|
|
#include "examples/font_bounds_example.h"
|
|
|
|
void setup() {
|
|
// Your setup code...
|
|
|
|
// Quick test
|
|
if (quickFontTest()) {
|
|
// Font bounds working
|
|
}
|
|
}
|
|
|
|
void loop() {
|
|
// Use font bounds functions as needed
|
|
if (oled.textFits(currentMessage)) {
|
|
int16_t x, y;
|
|
oled.getCenteredPosition(currentMessage, &x, &y);
|
|
oled.setCursor(x, y);
|
|
oled.print(currentMessage);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Option 2: App Integration
|
|
Add as an app in your app system:
|
|
```cpp
|
|
void fontBoundsApp() {
|
|
fontBoundsMenu();
|
|
|
|
// Handle encoder input for menu selection
|
|
// Run different demonstrations based on selection
|
|
}
|
|
```
|
|
|
|
## Example Functions Available
|
|
|
|
- `fontBoundsExample()` - Complete demonstration
|
|
- `testAllFonts()` - Font comparison
|
|
- `displayTextInfo(const char* text)` - Text analysis
|
|
- `quickFontTest()` - Validation test
|
|
- `fontBoundsMenu()` - Interactive menu
|
|
- `demonstrateTextWrapping(const char* text)` - Wrapping demo
|
|
- `demonstrateTextPositioning(const char* text)` - Alignment demo
|
|
|
|
## Tips for Best Results
|
|
|
|
1. **Always check fit before displaying**: Use `textFits()` or `textFitsAt()`
|
|
2. **Use appropriate fonts**: Different fonts for different purposes
|
|
3. **Consider word wrapping**: For long messages
|
|
4. **Center important text**: Use `getCenteredPosition()`
|
|
5. **Account for baselines**: Use `TextBounds.ascent` for proper positioning
|
|
6. **Test with different content**: Fonts behave differently with different characters
|
|
|
|
## Display Constraints
|
|
|
|
- **Screen**: 128x32 pixels
|
|
- **Available fonts**: 4 (Eurostile, Berkeley, Jokerman, Jumperless)
|
|
- **Font sizes**: Various heights from ~10px to ~47px
|
|
- **Max lines**: Depends on font (2-3 lines typically)
|
|
- **Max characters**: Varies by font (~10-20 chars per line)
|
|
|
|
This system ensures your text always displays perfectly on the OLED, regardless of content length or font choice! |