New TableField
This commit is contained in:
committed by
Raphael Coeffic
parent
4a77acf6af
commit
2eef7755a5
+50
-82
@@ -23,54 +23,19 @@
|
||||
|
||||
#include <lvgl/lvgl.h>
|
||||
|
||||
static void menu_clicked(lv_event_t *e)
|
||||
void MenuBody::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
|
||||
{
|
||||
lv_obj_t* target = lv_event_get_target(e);
|
||||
if (!target) return;
|
||||
|
||||
uint16_t row;
|
||||
uint16_t col;
|
||||
lv_table_get_selected_cell(target, &row, &col);
|
||||
|
||||
MenuBody* mb = (MenuBody*)lv_obj_get_user_data(target);
|
||||
if (!mb) return;
|
||||
|
||||
mb->onPress(row);
|
||||
}
|
||||
|
||||
void menu_draw_begin(lv_event_t* e)
|
||||
{
|
||||
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
|
||||
if (dsc->part != LV_PART_ITEMS) return;
|
||||
|
||||
lv_obj_t* table = lv_event_get_target(e);
|
||||
if (!table) return;
|
||||
|
||||
MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table);
|
||||
if (!mb) return;
|
||||
|
||||
uint16_t row = dsc->id;
|
||||
lv_canvas_t* icon = (lv_canvas_t*)mb->lines[row].getIcon();
|
||||
lv_canvas_t* icon = (lv_canvas_t*)lines[row].getIcon();
|
||||
if (!icon) return;
|
||||
|
||||
lv_img_t* img = &icon->img;
|
||||
lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS);
|
||||
lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS);
|
||||
dsc->label_dsc->ofs_x = img->w + cell_left;
|
||||
}
|
||||
|
||||
void menu_draw_end(lv_event_t* e)
|
||||
void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
|
||||
{
|
||||
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
|
||||
if (dsc->part != LV_PART_ITEMS) return;
|
||||
|
||||
lv_obj_t* table = lv_event_get_target(e);
|
||||
if (!table) return;
|
||||
|
||||
MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table);
|
||||
if (!mb) return;
|
||||
|
||||
uint16_t row = dsc->id;
|
||||
lv_obj_t* icon = mb->lines[row].getIcon();
|
||||
lv_obj_t* icon = lines[row].getIcon();
|
||||
if (!icon) return;
|
||||
|
||||
lv_draw_img_dsc_t img_dsc;
|
||||
@@ -81,7 +46,7 @@ void menu_draw_end(lv_event_t* e)
|
||||
|
||||
lv_coord_t area_h = lv_area_get_height(dsc->draw_area);
|
||||
|
||||
lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS);
|
||||
lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS);
|
||||
coords.x1 = dsc->draw_area->x1 + cell_left;
|
||||
coords.x2 = coords.x1 + img->header.w - 1;
|
||||
coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2;
|
||||
@@ -91,13 +56,10 @@ void menu_draw_end(lv_event_t* e)
|
||||
}
|
||||
|
||||
MenuBody::MenuBody(Window * parent, const rect_t & rect):
|
||||
Window(parent, rect, OPAQUE, 0, lv_table_create)
|
||||
TableField(parent, rect)
|
||||
{
|
||||
lv_table_set_col_cnt(lvobj, 1);
|
||||
lv_table_set_col_width(lvobj, 0, rect.w);
|
||||
lv_obj_add_event_cb(lvobj, menu_clicked, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
lv_obj_add_event_cb(lvobj, menu_draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr);
|
||||
lv_obj_add_event_cb(lvobj, menu_draw_end, LV_EVENT_DRAW_PART_END, nullptr);
|
||||
setColumnCount(1);
|
||||
setColumnWidth(0, rect.w);
|
||||
}
|
||||
|
||||
void MenuBody::addLine(const std::string &text, std::function<void()> onPress,
|
||||
@@ -128,7 +90,7 @@ void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text,
|
||||
void MenuBody::removeLines()
|
||||
{
|
||||
lines.clear();
|
||||
lv_obj_clean(lvobj);
|
||||
setRowCount(0);
|
||||
}
|
||||
|
||||
coord_t MenuBody::getContentHeight()
|
||||
@@ -200,6 +162,11 @@ void MenuBody::setIndex(int index)
|
||||
}
|
||||
}
|
||||
|
||||
void MenuBody::onPress(uint16_t row, uint16_t col)
|
||||
{
|
||||
onPress(row);
|
||||
}
|
||||
|
||||
void MenuBody::selectNext(MENU_DIRECTION direction)
|
||||
{
|
||||
// look for the next non separator line
|
||||
@@ -211,40 +178,41 @@ void MenuBody::selectNext(MENU_DIRECTION direction)
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void MenuBody::onEvent(event_t event)
|
||||
{
|
||||
TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event);
|
||||
// TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event);
|
||||
|
||||
if (event == EVT_ROTARY_RIGHT) {
|
||||
if (!lines.empty()) {
|
||||
selectNext(DIRECTION_UP);
|
||||
onKeyPress();
|
||||
}
|
||||
}
|
||||
else if (event == EVT_ROTARY_LEFT) {
|
||||
if (!lines.empty()) {
|
||||
selectNext(DIRECTION_DOWN);
|
||||
onKeyPress();
|
||||
}
|
||||
}
|
||||
else if (event == EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
if (!lines.empty()) {
|
||||
onKeyPress();
|
||||
if (selectedIndex < 0) {
|
||||
setIndex(0);
|
||||
}
|
||||
else {
|
||||
Menu * menu = getParentMenu();
|
||||
if (menu->multiple) {
|
||||
lines[selectedIndex].onPress();
|
||||
menu->invalidate();
|
||||
}
|
||||
else {
|
||||
lines[selectedIndex].onPress();
|
||||
menu->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
// if (event == EVT_ROTARY_RIGHT) {
|
||||
// if (!lines.empty()) {
|
||||
// selectNext(DIRECTION_UP);
|
||||
// onKeyPress();
|
||||
// }
|
||||
// }
|
||||
// else if (event == EVT_ROTARY_LEFT) {
|
||||
// if (!lines.empty()) {
|
||||
// selectNext(DIRECTION_DOWN);
|
||||
// onKeyPress();
|
||||
// }
|
||||
// }
|
||||
// else if (event == EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
// if (!lines.empty()) {
|
||||
// onKeyPress();
|
||||
// if (selectedIndex < 0) {
|
||||
// setIndex(0);
|
||||
// }
|
||||
// else {
|
||||
// Menu * menu = getParentMenu();
|
||||
// if (menu->multiple) {
|
||||
// lines[selectedIndex].onPress();
|
||||
// menu->invalidate();
|
||||
// }
|
||||
// else {
|
||||
// lines[selectedIndex].onPress();
|
||||
// menu->deleteLater();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
if (event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
onKeyPress();
|
||||
if (onCancel) {
|
||||
onCancel();
|
||||
@@ -252,7 +220,7 @@ void MenuBody::onEvent(event_t event)
|
||||
Window::onEvent(event);
|
||||
}
|
||||
else {
|
||||
Window::onEvent(event);
|
||||
TableField::onEvent(event);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+6
-3
@@ -23,7 +23,9 @@
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
#include "modal_window.h"
|
||||
#include "table.h"
|
||||
|
||||
class Menu;
|
||||
class MenuWindowContent;
|
||||
@@ -35,7 +37,7 @@ struct lvobj_delete {
|
||||
}
|
||||
};
|
||||
|
||||
class MenuBody: public Window
|
||||
class MenuBody: public TableField
|
||||
{
|
||||
friend class MenuWindowContent;
|
||||
friend class Menu;
|
||||
@@ -113,8 +115,9 @@ class MenuBody: public Window
|
||||
void onPress(size_t index);
|
||||
|
||||
protected:
|
||||
friend void menu_draw_begin(lv_event_t *);
|
||||
friend void menu_draw_end(lv_event_t *);
|
||||
void onPress(uint16_t row, uint16_t col) override;
|
||||
void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override;
|
||||
void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override;
|
||||
|
||||
void selectNext(MENU_DIRECTION direction);
|
||||
int rangeCheck(int);
|
||||
|
||||
+170
-113
@@ -18,146 +18,203 @@
|
||||
*/
|
||||
|
||||
#include "table.h"
|
||||
#include "font.h"
|
||||
|
||||
void Table::Header::paint(BitmapBuffer * dc)
|
||||
void TableField::clicked(lv_event_t *e)
|
||||
{
|
||||
coord_t x = 10;
|
||||
if (!cells.empty()) {
|
||||
dc->clear(COLOR_THEME_FOCUS);
|
||||
for (unsigned i = 0; i < cells.size(); i++) {
|
||||
auto cell = cells[i];
|
||||
if (cell) {
|
||||
cell->paint(dc, x, 0, TABLE_HEADER_FONT);
|
||||
}
|
||||
x += static_cast<Table *>(parent)->columnsWidth[i];
|
||||
}
|
||||
}
|
||||
lv_obj_t* target = lv_event_get_target(e);
|
||||
if (!target) return;
|
||||
|
||||
uint16_t row;
|
||||
uint16_t col;
|
||||
lv_table_get_selected_cell(target, &row, &col);
|
||||
|
||||
TableField* tf = (TableField*)lv_obj_get_user_data(target);
|
||||
if (!tf) return;
|
||||
|
||||
tf->onPress(row, col);
|
||||
}
|
||||
|
||||
void Table::Body::checkEvents()
|
||||
void TableField::draw_begin(lv_event_t* e)
|
||||
{
|
||||
Window::checkEvents();
|
||||
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
|
||||
if (dsc->part != LV_PART_ITEMS) return;
|
||||
|
||||
if (_deleted)
|
||||
lv_obj_t* table = lv_event_get_target(e);
|
||||
if (!table) return;
|
||||
|
||||
uint16_t cols = lv_table_get_col_cnt(table);
|
||||
if (cols == 0) return;
|
||||
|
||||
TableField* tf = (TableField*)lv_obj_get_user_data(table);
|
||||
if (!tf) return;
|
||||
|
||||
uint16_t row = dsc->id / cols;
|
||||
uint16_t col = dsc->id % cols;
|
||||
tf->onDrawBegin(row, col, dsc);
|
||||
}
|
||||
|
||||
void TableField::draw_end(lv_event_t* e)
|
||||
{
|
||||
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
|
||||
if (dsc->part != LV_PART_ITEMS) return;
|
||||
|
||||
lv_obj_t* table = lv_event_get_target(e);
|
||||
if (!table) return;
|
||||
|
||||
uint16_t cols = lv_table_get_col_cnt(table);
|
||||
if (cols == 0) return;
|
||||
|
||||
TableField* tf = (TableField*)lv_obj_get_user_data(table);
|
||||
if (!tf) return;
|
||||
|
||||
uint16_t row = dsc->id / cols;
|
||||
uint16_t col = dsc->id % cols;
|
||||
tf->onDrawEnd(row, col, dsc);
|
||||
}
|
||||
|
||||
TableField::TableField(Window* parent, const rect_t& rect, WindowFlags windowFlags) :
|
||||
FormField(parent, rect, windowFlags, 0, lv_table_create)
|
||||
{
|
||||
lv_obj_add_event_cb(lvobj, TableField::clicked, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
lv_obj_add_event_cb(lvobj, TableField::draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr);
|
||||
lv_obj_add_event_cb(lvobj, TableField::draw_end, LV_EVENT_DRAW_PART_END, nullptr);
|
||||
}
|
||||
|
||||
void TableField::setColumnCount(uint16_t cols)
|
||||
{
|
||||
lv_table_set_col_cnt(lvobj, cols);
|
||||
}
|
||||
|
||||
uint16_t TableField::getColumnCount() const
|
||||
{
|
||||
return lv_table_get_col_cnt(lvobj);
|
||||
}
|
||||
|
||||
void TableField::setRowCount(uint16_t rows)
|
||||
{
|
||||
lv_table_set_row_cnt(lvobj, rows);
|
||||
}
|
||||
|
||||
uint16_t TableField::getRowCount() const
|
||||
{
|
||||
return lv_table_get_row_cnt(lvobj);
|
||||
}
|
||||
|
||||
void TableField::setColumnWidth(uint16_t col, coord_t w)
|
||||
{
|
||||
lv_table_set_col_width(lvobj, col, w);
|
||||
}
|
||||
|
||||
coord_t TableField::getColumnWidth(uint16_t col) const
|
||||
{
|
||||
return lv_table_get_col_width(lvobj, col);
|
||||
}
|
||||
|
||||
void TableField::select(uint16_t row, uint16_t col)
|
||||
{
|
||||
lv_table_t* table = (lv_table_t*)lvobj;
|
||||
if (table->row_act == row && table->col_act == row) return;
|
||||
|
||||
if (row >= table->row_cnt || col >= table->col_cnt) {
|
||||
table->col_act = LV_TABLE_CELL_NONE;
|
||||
table->row_act = LV_TABLE_CELL_NONE;
|
||||
return;
|
||||
|
||||
coord_t y = 0;
|
||||
for (auto line: lines) {
|
||||
coord_t x = 10;
|
||||
for (unsigned i = 0; i < line->cells.size(); i++) {
|
||||
auto cell = line->cells[i];
|
||||
auto width = static_cast<Table *>(parent)->columnsWidth[i];
|
||||
if (cell && cell->needsInvalidate()) {
|
||||
invalidate({x, y, width, TABLE_LINE_HEIGHT - 2});
|
||||
}
|
||||
x += width;
|
||||
}
|
||||
y += TABLE_LINE_HEIGHT;
|
||||
}
|
||||
|
||||
table->row_act = row;
|
||||
table->col_act = col;
|
||||
|
||||
lv_obj_invalidate(lvobj);
|
||||
adjustScroll();
|
||||
}
|
||||
|
||||
void Table::Body::paint(BitmapBuffer * dc)
|
||||
void TableField::adjustScroll()
|
||||
{
|
||||
coord_t y = 0;
|
||||
int index = 0;
|
||||
dc->clear(COLOR_THEME_SECONDARY3);
|
||||
for (auto line: lines) {
|
||||
bool highlight = (index == selection);
|
||||
dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? COLOR_THEME_FOCUS : COLOR_THEME_SECONDARY3);
|
||||
coord_t x = 10;
|
||||
for (unsigned i = 0; i < line->cells.size(); i++) {
|
||||
auto cell = line->cells[i];
|
||||
if (cell) {
|
||||
cell->paint(dc, x, y, line->flags + (highlight ? COLOR_THEME_PRIMARY2 - COLOR_MASK(line->flags) : COLOR_THEME_SECONDARY1));
|
||||
}
|
||||
x += static_cast<Table *>(parent)->columnsWidth[i];
|
||||
}
|
||||
y += TABLE_LINE_HEIGHT;
|
||||
index += 1;
|
||||
lv_table_t* table = (lv_table_t*)lvobj;
|
||||
|
||||
// only vertical scroll for now
|
||||
lv_coord_t h_before = 0;
|
||||
for (uint16_t i = 0; i < table->row_act; i++) h_before += table->row_h[i];
|
||||
|
||||
lv_coord_t row_h = table->row_h[table->row_act];
|
||||
lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj);
|
||||
|
||||
lv_obj_update_layout(lvobj);
|
||||
lv_coord_t h = lv_obj_get_height(lvobj);
|
||||
|
||||
lv_coord_t diff_y = 0;
|
||||
if (h_before < scroll_y) {
|
||||
diff_y = scroll_y - h_before;
|
||||
} else if (scroll_y + h < h_before + row_h) {
|
||||
diff_y = scroll_y + h - h_before - row_h;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_obj_scroll_by_bounded(lvobj, 0, diff_y, LV_ANIM_OFF);
|
||||
}
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
bool Table::Body::onTouchEnd(coord_t x, coord_t y)
|
||||
void TableField::selectNext(int16_t dir)
|
||||
{
|
||||
unsigned index = y / TABLE_LINE_HEIGHT;
|
||||
if (index < lines.size()) {
|
||||
onKeyPress();
|
||||
setFocus(SET_FOCUS_DEFAULT);
|
||||
auto onPress = lines[index]->onPress;
|
||||
if (onPress)
|
||||
onPress();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
lv_table_t* table = (lv_table_t*)lvobj;
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void Table::Body::onEvent(event_t event)
|
||||
{
|
||||
TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event);
|
||||
if (table->col_act == LV_TABLE_CELL_NONE ||
|
||||
table->row_act == LV_TABLE_CELL_NONE) {
|
||||
|
||||
if (event == EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
if (selection >= 0) {
|
||||
onKeyPress();
|
||||
auto onPress = lines[selection]->onPress;
|
||||
if (onPress)
|
||||
onPress();
|
||||
if (table->col_cnt > 0 && table->row_cnt > 0) {
|
||||
table->col_act = 0;
|
||||
table->row_act = 0;
|
||||
}
|
||||
}
|
||||
if (event == EVT_ROTARY_RIGHT) {
|
||||
onKeyPress();
|
||||
auto table = static_cast<Table *>(parent);
|
||||
if (table->getWindowFlags() & FORWARD_SCROLL) {
|
||||
auto index = selection + 1;
|
||||
if (index < int(lines.size())) {
|
||||
select(index, true);
|
||||
}
|
||||
else {
|
||||
auto next = table->getNextField();
|
||||
if (next) {
|
||||
next->setFocus(SET_FOCUS_FORWARD, this);
|
||||
if (!hasFocus()) {
|
||||
select(-1, false);
|
||||
} else {
|
||||
table->col_act += dir;
|
||||
if (table->col_act >= table->col_cnt) {
|
||||
|
||||
table->col_act = 0;
|
||||
table->row_act += dir;
|
||||
|
||||
if (table->row_act >= table->row_cnt) {
|
||||
table->col_act = LV_TABLE_CELL_NONE;
|
||||
table->row_act = LV_TABLE_CELL_NONE;
|
||||
|
||||
// wrap around
|
||||
if (table->col_cnt > 0 && table->row_cnt > 0) {
|
||||
|
||||
if (dir < 0) {
|
||||
table->col_act = table->col_cnt - 1;
|
||||
table->row_act = table->row_cnt - 1;
|
||||
} else {
|
||||
table->col_act = 0;
|
||||
table->row_act = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!lines.empty()) {
|
||||
select((selection + 1) % lines.size(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lv_obj_invalidate(lvobj);
|
||||
adjustScroll();
|
||||
}
|
||||
|
||||
void TableField::onEvent(event_t event)
|
||||
{
|
||||
if (event == EVT_ROTARY_RIGHT) {
|
||||
onKeyPress();
|
||||
selectNext(1);
|
||||
}
|
||||
else if (event == EVT_ROTARY_LEFT) {
|
||||
onKeyPress();
|
||||
auto table = static_cast<Table *>(parent);
|
||||
if (table->getWindowFlags() & FORWARD_SCROLL) {
|
||||
auto index = selection - 1;
|
||||
if (index >= 0) {
|
||||
select(index, true);
|
||||
}
|
||||
else {
|
||||
auto previous = table->getPreviousField();
|
||||
if (previous) {
|
||||
select(-1, false);
|
||||
previous->setFocus(SET_FOCUS_BACKWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!lines.empty()) {
|
||||
select(selection <= 0 ? lines.size() - 1 : selection - 1, true);
|
||||
}
|
||||
}
|
||||
selectNext(-1);
|
||||
}
|
||||
else if (event == EVT_KEY_BREAK(KEY_EXIT) && selection >= 0) {
|
||||
select(-1, true);
|
||||
Window::onEvent(event);
|
||||
else if (event == EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
onKeyPress();
|
||||
|
||||
uint16_t row;
|
||||
uint16_t col;
|
||||
lv_table_get_selected_cell(lvobj, &row, &col);
|
||||
|
||||
onPress(row, col);
|
||||
}
|
||||
else {
|
||||
Window::onEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+24
-339
@@ -19,354 +19,39 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "form.h"
|
||||
#include "libopenui_config.h"
|
||||
#include "font.h"
|
||||
|
||||
class Table: public FormField
|
||||
class TableField : public FormField
|
||||
{
|
||||
public:
|
||||
class Cell
|
||||
{
|
||||
public:
|
||||
virtual ~Cell() = default;
|
||||
|
||||
virtual void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) = 0;
|
||||
|
||||
virtual bool needsInvalidate() = 0;
|
||||
};
|
||||
|
||||
class StringCell : public Cell
|
||||
{
|
||||
public:
|
||||
StringCell() = default;
|
||||
|
||||
explicit StringCell(std::string value):
|
||||
value(std::move(value))
|
||||
{
|
||||
}
|
||||
|
||||
void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override
|
||||
{
|
||||
dc->drawText(x, y - 2 + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2 + 3, value.c_str(), flags);
|
||||
}
|
||||
|
||||
bool needsInvalidate() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class DynamicStringCell : public Cell
|
||||
{
|
||||
public:
|
||||
explicit DynamicStringCell(std::function<std::string()> getText):
|
||||
getText(std::move(getText))
|
||||
{
|
||||
}
|
||||
|
||||
void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override
|
||||
{
|
||||
auto text = getText();
|
||||
dc->drawText(x, y - 2 + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, text.c_str(), SPACING_NUMBERS_CONST | flags);
|
||||
}
|
||||
|
||||
bool needsInvalidate() override
|
||||
{
|
||||
return true; // TODO optimize this
|
||||
}
|
||||
|
||||
protected:
|
||||
std::function<std::string()> getText;
|
||||
};
|
||||
|
||||
class CustomCell : public Cell
|
||||
{
|
||||
public:
|
||||
explicit CustomCell(std::function<void(BitmapBuffer * /*dc*/, coord_t /*x*/, coord_t /*y*/, LcdFlags /*flags*/)> paintFunction):
|
||||
paintFunction(std::move(paintFunction))
|
||||
{
|
||||
}
|
||||
|
||||
void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override
|
||||
{
|
||||
paintFunction(dc, x, y, flags);
|
||||
}
|
||||
|
||||
bool needsInvalidate() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::function<void(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags)> paintFunction;
|
||||
};
|
||||
|
||||
class Line
|
||||
{
|
||||
public:
|
||||
explicit Line(uint8_t columnsCount):
|
||||
cells(columnsCount, nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~Line()
|
||||
{
|
||||
for (auto cell: cells) {
|
||||
delete cell;
|
||||
}
|
||||
}
|
||||
std::vector<Cell *> cells;
|
||||
std::function<void()> onPress;
|
||||
std::function<void()> onSelect;
|
||||
LcdFlags flags = TABLE_BODY_FONT;
|
||||
};
|
||||
|
||||
class Header: public Window, public Line
|
||||
{
|
||||
public:
|
||||
Header(Table * parent, const rect_t & rect, uint8_t columnsCount):
|
||||
Window(parent, rect, OPAQUE),
|
||||
Line(columnsCount)
|
||||
{
|
||||
}
|
||||
public:
|
||||
TableField(Window* parent, const rect_t& rect,
|
||||
WindowFlags windowFlags = OPAQUE | FORM_NO_BORDER);
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
std::string getName() const override
|
||||
{
|
||||
return "Table::Header";
|
||||
}
|
||||
std::string getName() const override { return "Table"; }
|
||||
#endif
|
||||
|
||||
void paint(BitmapBuffer * dc) override;
|
||||
};
|
||||
void setColumnCount(uint16_t cols);
|
||||
uint16_t getColumnCount() const;
|
||||
|
||||
void setRowCount(uint16_t rows);
|
||||
uint16_t getRowCount() const;
|
||||
|
||||
void setColumnWidth(uint16_t col, coord_t w);
|
||||
coord_t getColumnWidth(uint16_t col) const;
|
||||
|
||||
class Body: public Window
|
||||
{
|
||||
friend class Table;
|
||||
void select(uint16_t row, uint16_t col);
|
||||
virtual void onPress(uint16_t row, uint16_t col) {}
|
||||
|
||||
public:
|
||||
Body(Table * parent, const rect_t & rect, WindowFlags windowFlags):
|
||||
Window(parent, rect, windowFlags)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void adjustScroll();
|
||||
void selectNext(int16_t dir);
|
||||
void onEvent(event_t event) override;
|
||||
|
||||
~Body() override
|
||||
{
|
||||
clear();
|
||||
}
|
||||
virtual void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {}
|
||||
virtual void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {}
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
std::string getName() const override
|
||||
{
|
||||
return "Table::Body";
|
||||
}
|
||||
#endif
|
||||
|
||||
void addLine(Line * line)
|
||||
{
|
||||
lines.push_back(line);
|
||||
if (hasFocus() && selection < 0) {
|
||||
select(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
void setLineFlags(uint8_t index, LcdFlags flags)
|
||||
{
|
||||
if (lines[index]->flags != flags) {
|
||||
lines[index]->flags = flags;
|
||||
invalidate({0, index * TABLE_LINE_HEIGHT, width(), TABLE_LINE_HEIGHT});
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (auto line: lines) {
|
||||
delete line;
|
||||
}
|
||||
lines.clear();
|
||||
}
|
||||
|
||||
void select(int index, bool scroll)
|
||||
{
|
||||
selection = index;
|
||||
if (scroll) {
|
||||
coord_t y = index * TABLE_LINE_HEIGHT;
|
||||
Window * window = this;
|
||||
while (window->getWindowFlags() & FORWARD_SCROLL) {
|
||||
y += window->top();
|
||||
window = window->getParent();
|
||||
}
|
||||
const rect_t rect = {
|
||||
0,
|
||||
y,
|
||||
width(),
|
||||
TABLE_LINE_HEIGHT
|
||||
};
|
||||
window->scrollTo(rect);
|
||||
}
|
||||
invalidate();
|
||||
if (index >= 0) {
|
||||
auto onSelect = lines[index]->onSelect;
|
||||
if (onSelect) {
|
||||
onSelect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void paint(BitmapBuffer * dc) override;
|
||||
|
||||
#if defined(HARDWARE_KEYS)
|
||||
void onEvent(event_t event) override;
|
||||
#endif
|
||||
|
||||
#if defined(HARDWARE_TOUCH)
|
||||
bool onTouchEnd(coord_t x, coord_t y) override;
|
||||
#endif
|
||||
|
||||
void checkEvents() override;
|
||||
|
||||
protected:
|
||||
std::vector<Line *> lines;
|
||||
int selection = -1;
|
||||
};
|
||||
|
||||
public:
|
||||
Table(Window * parent, const rect_t & rect, uint8_t columnsCount, WindowFlags windowFlags = OPAQUE | FORM_NO_BORDER):
|
||||
FormField(parent, rect, windowFlags),
|
||||
columnsCount(columnsCount),
|
||||
columnsWidth(columnsCount, width() / columnsCount),
|
||||
header(this, {0, 0, width(), 0}, columnsCount),
|
||||
body(this, {0, 0, width(), height()}, windowFlags)
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
std::string getName() const override
|
||||
{
|
||||
return "Table";
|
||||
}
|
||||
#endif
|
||||
|
||||
void deleteLater(bool detach = true, bool trash = true) override // NOLINT(google-default-arguments)
|
||||
{
|
||||
if (_deleted)
|
||||
return;
|
||||
|
||||
header.deleteLater(true, false);
|
||||
body.deleteLater(true, false);
|
||||
|
||||
FormField::deleteLater(detach, trash);
|
||||
}
|
||||
|
||||
void setColumnsWidth(const coord_t values[])
|
||||
{
|
||||
int restColumn = -1;
|
||||
coord_t restWidth = width();
|
||||
for (uint8_t i = 0; i < columnsCount; i++) {
|
||||
auto columnWidth = values[i];
|
||||
columnsWidth[i] = columnWidth;
|
||||
if (columnWidth == 0) {
|
||||
restColumn = i;
|
||||
}
|
||||
else {
|
||||
restWidth -= columnWidth;
|
||||
}
|
||||
}
|
||||
if (restColumn >= 0) {
|
||||
columnsWidth[restColumn] = restWidth;
|
||||
}
|
||||
}
|
||||
|
||||
int getSelection() const
|
||||
{
|
||||
return body.selection;
|
||||
}
|
||||
|
||||
void clearSelection()
|
||||
{
|
||||
body.selection = -1;
|
||||
body.invalidate();
|
||||
}
|
||||
|
||||
void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override // NOLINT(google-default-arguments)
|
||||
{
|
||||
if (body.lines.empty()) {
|
||||
if (flag == SET_FOCUS_BACKWARD) {
|
||||
if (previous) {
|
||||
previous->setFocus(flag, this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (next) {
|
||||
next->setFocus(flag, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
body.setFocus(flag, from);
|
||||
if (body.selection < 0) {
|
||||
select(flag == SET_FOCUS_BACKWARD ? (int)body.lines.size() - 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void select(int index, bool scroll = true)
|
||||
{
|
||||
body.select(index, scroll);
|
||||
}
|
||||
|
||||
void setLineFlags(uint8_t index, LcdFlags flags)
|
||||
{
|
||||
body.setLineFlags(index, flags);
|
||||
}
|
||||
|
||||
void setHeader(const char * const values[])
|
||||
{
|
||||
header.setHeight(TABLE_HEADER_HEIGHT);
|
||||
body.setTop(TABLE_HEADER_HEIGHT);
|
||||
body.setHeight(height() - TABLE_HEADER_HEIGHT);
|
||||
for (uint8_t i = 0; i < columnsCount; i++) {
|
||||
header.cells[i] = new StringCell(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void addLine(Line * line, std::function<void()> onPress = nullptr, std::function<void()> onSelect = nullptr)
|
||||
{
|
||||
line->onPress = std::move(onPress);
|
||||
line->onSelect = std::move(onSelect);
|
||||
body.addLine(line);
|
||||
}
|
||||
|
||||
void addLine(const char * const values[], std::function<void()> onPress = nullptr, std::function<void()> onSelect = nullptr)
|
||||
{
|
||||
Line * line = new Line(columnsCount);
|
||||
for (uint8_t i = 0; i < columnsCount; i++) {
|
||||
line->cells[i] = new StringCell(values[i]);
|
||||
}
|
||||
addLine(line, std::move(onPress), std::move(onSelect));
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
clearSelection();
|
||||
body.clear();
|
||||
}
|
||||
|
||||
uint8_t size() const
|
||||
{
|
||||
return body.lines.size();
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t columnsCount;
|
||||
std::vector<coord_t> columnsWidth;
|
||||
Header header;
|
||||
Body body;
|
||||
static void clicked(lv_event_t *e);
|
||||
static void draw_begin(lv_event_t *e);
|
||||
static void draw_end(lv_event_t *e);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user