blob: d6f4901dc9a10a2a0a455eaea9d4d2ff74cb7945 [file] [log] [blame]
// Copyright 2024 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_draw/text_area.h"
#include "pw_color/color.h"
#include "pw_draw/draw.h"
#include "pw_draw/font_set.h"
#include "pw_framebuffer/framebuffer.h"
#include "pw_framebuffer/writer.h"
using pw::color::color_rgb565_t;
using pw::framebuffer::FramebufferWriter;
using pw::geometry::Vector2;
namespace pw::draw {
TextArea::TextArea(pw::framebuffer::Framebuffer& fb, const FontSet& font)
: font_(font), framebuffer_(fb) {
// SetFont(font);
// Default colors: White on Black
character_wrap_enabled_ = true;
foreground_color_ = 0xFFFF;
background_color_ = 0;
SetCursor(0, 0);
}
void TextArea::SetCursor(int x, int y) {
cursor_x_ = x;
cursor_y_ = y;
column_count_ = 0;
}
void TextArea::SetForegroundColor(color_rgb565_t color) {
foreground_color_ = color;
}
void TextArea::SetBackgroundColor(color_rgb565_t color) {
background_color_ = color;
}
void TextArea::SetCharacterWrap(bool new_setting) {
character_wrap_enabled_ = new_setting;
}
void TextArea::MoveCursorRightOnce() {
cursor_x_ = cursor_x_ + font_.width;
column_count_++;
}
void TextArea::InsertLineBreak() {
cursor_y_ = cursor_y_ + font_.height;
cursor_x_ = cursor_x_ - (column_count_ * font_.width);
column_count_ = 0;
if (cursor_y_ >= framebuffer_.size().height) {
ScrollUp(1);
cursor_y_ = cursor_y_ - font_.height;
}
}
void TextArea::DrawCharacter(int character) {
if (character == '\n') {
InsertLineBreak();
return;
}
if ((int)character < font_.starting_character ||
(int)character > font_.ending_character) {
// Unprintable character
MoveCursorRightOnce();
return;
}
if (character_wrap_enabled_ &&
(font_.width + cursor_x_) > framebuffer_.size().width) {
InsertLineBreak();
}
pw::draw::DrawCharacter(character,
Vector2<int>{cursor_x_, cursor_y_},
foreground_color_,
background_color_,
font_,
framebuffer_);
// Move cursor to the right by 1 glyph.
MoveCursorRightOnce();
}
void TextArea::DrawCharacter(int character, int x, int y) {
SetCursor(x, y);
DrawCharacter(character);
}
void TextArea::DrawTestFontSheet(int character_column_width, int x, int y) {
SetCursor(x, y);
for (int c = font_.starting_character; c <= font_.ending_character; c++) {
int index = c - font_.starting_character;
if (index > 0 && index % character_column_width == 0) {
DrawCharacter('\n');
}
DrawCharacter(c);
}
}
// DrawText at x, y (upper left pixel of font). Carriage returns will move
// text to the next line.
void TextArea::DrawText(const char* str) {
for (const char* ch = str; *ch != '\0'; ch++) {
DrawCharacter(*ch);
}
}
void TextArea::DrawText(const char* str, int x, int y) {
SetCursor(x, y);
DrawText(str);
}
void TextArea::DrawText(const wchar_t* str) {
for (const wchar_t* ch = str; *ch != L'\0'; ch++) {
DrawCharacter(*ch);
}
}
void TextArea::DrawText(const wchar_t* str, int x, int y) {
SetCursor(x, y);
DrawText(str);
}
void TextArea::ScrollUp(int lines) {
int pixel_height = lines * font_.height;
int start_x = 0;
int start_y = pixel_height;
FramebufferWriter writer(framebuffer_);
for (int current_x_ = 0; current_x_ < framebuffer_.size().width;
current_x_++) {
for (int current_y_ = start_y; current_y_ < framebuffer_.size().height;
current_y_++) {
if (auto pixel_color = writer.GetPixel(current_x_, current_y_);
pixel_color.ok()) {
writer.SetPixel(
start_x + current_x_, current_y_ - start_y, *pixel_color);
}
}
}
// Draw a filled background_color rectangle at the bottom to erase the old
// text.
for (int x = 0; x < framebuffer_.size().width; x++) {
for (int y = framebuffer_.size().height - pixel_height;
y < framebuffer_.size().height;
y++) {
writer.SetPixel(x, y, background_color_);
}
}
}
} // namespace pw::draw