From 9b14d177c579dff6599aa4fe1b9c49b7723c0004 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 2 Oct 2019 11:05:12 +0200 Subject: [PATCH] Initial commit --- radio/src/thirdparty/libopenui/CMakeLists.txt | 1 + .../thirdparty/libopenui/src/CMakeLists.txt | 31 + .../thirdparty/libopenui/src/basenumberedit.h | 104 ++ radio/src/thirdparty/libopenui/src/bitfield.h | 85 ++ .../thirdparty/libopenui/src/bitmapbuffer.cpp | 925 ++++++++++++++++++ .../thirdparty/libopenui/src/bitmapbuffer.h | 489 +++++++++ radio/src/thirdparty/libopenui/src/button.cpp | 138 +++ radio/src/thirdparty/libopenui/src/button.h | 180 ++++ .../src/thirdparty/libopenui/src/checkbox.cpp | 51 + radio/src/thirdparty/libopenui/src/checkbox.h | 58 ++ radio/src/thirdparty/libopenui/src/choice.cpp | 118 +++ radio/src/thirdparty/libopenui/src/choice.h | 74 ++ .../thirdparty/libopenui/src/coloredit.cpp | 85 ++ .../src/thirdparty/libopenui/src/coloredit.h | 39 + radio/src/thirdparty/libopenui/src/dialog.cpp | 49 + radio/src/thirdparty/libopenui/src/dialog.h | 38 + .../thirdparty/libopenui/src/filechoice.cpp | 158 +++ .../src/thirdparty/libopenui/src/filechoice.h | 57 ++ radio/src/thirdparty/libopenui/src/font.h | 30 + radio/src/thirdparty/libopenui/src/form.cpp | 120 +++ radio/src/thirdparty/libopenui/src/form.h | 197 ++++ .../src/thirdparty/libopenui/src/gridlayout.h | 123 +++ .../thirdparty/libopenui/src/keyboard_base.h | 77 ++ .../libopenui/src/keyboard_number.cpp | 97 ++ .../libopenui/src/keyboard_number.h | 54 + .../libopenui/src/keyboard_text.cpp | 228 +++++ .../thirdparty/libopenui/src/keyboard_text.h | 67 ++ .../libopenui/src/libopenui_defines.h | 125 +++ .../libopenui/src/libopenui_depends.h | 33 + .../libopenui/src/libopenui_file.cpp | 80 ++ .../thirdparty/libopenui/src/libopenui_file.h | 32 + .../libopenui/src/libopenui_globals.cpp | 23 + .../libopenui/src/libopenui_globals.h | 30 + .../libopenui/src/libopenui_helpers.cpp | 21 + .../libopenui/src/libopenui_helpers.h | 57 ++ .../libopenui/src/libopenui_types.h | 63 ++ .../thirdparty/libopenui/src/mainwindow.cpp | 111 +++ .../src/thirdparty/libopenui/src/mainwindow.h | 63 ++ radio/src/thirdparty/libopenui/src/menu.cpp | 148 +++ radio/src/thirdparty/libopenui/src/menu.h | 196 ++++ .../thirdparty/libopenui/src/menutoolbar.cpp | 32 + .../thirdparty/libopenui/src/menutoolbar.h | 180 ++++ .../thirdparty/libopenui/src/numberedit.cpp | 115 +++ .../src/thirdparty/libopenui/src/numberedit.h | 89 ++ radio/src/thirdparty/libopenui/src/page.cpp | 100 ++ radio/src/thirdparty/libopenui/src/page.h | 89 ++ .../src/thirdparty/libopenui/src/progress.cpp | 26 + radio/src/thirdparty/libopenui/src/progress.h | 51 + radio/src/thirdparty/libopenui/src/slider.cpp | 81 ++ radio/src/thirdparty/libopenui/src/slider.h | 72 ++ radio/src/thirdparty/libopenui/src/static.cpp | 31 + radio/src/thirdparty/libopenui/src/static.h | 107 ++ .../thirdparty/libopenui/src/tabsgroup.cpp | 194 ++++ .../src/thirdparty/libopenui/src/tabsgroup.h | 191 ++++ .../src/thirdparty/libopenui/src/textedit.cpp | 168 ++++ radio/src/thirdparty/libopenui/src/textedit.h | 100 ++ radio/src/thirdparty/libopenui/src/theme.h | 45 + radio/src/thirdparty/libopenui/src/window.cpp | 395 ++++++++ radio/src/thirdparty/libopenui/src/window.h | 323 ++++++ 59 files changed, 7044 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/CMakeLists.txt create mode 100644 radio/src/thirdparty/libopenui/src/CMakeLists.txt create mode 100644 radio/src/thirdparty/libopenui/src/basenumberedit.h create mode 100644 radio/src/thirdparty/libopenui/src/bitfield.h create mode 100644 radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp create mode 100644 radio/src/thirdparty/libopenui/src/bitmapbuffer.h create mode 100644 radio/src/thirdparty/libopenui/src/button.cpp create mode 100644 radio/src/thirdparty/libopenui/src/button.h create mode 100644 radio/src/thirdparty/libopenui/src/checkbox.cpp create mode 100644 radio/src/thirdparty/libopenui/src/checkbox.h create mode 100644 radio/src/thirdparty/libopenui/src/choice.cpp create mode 100644 radio/src/thirdparty/libopenui/src/choice.h create mode 100644 radio/src/thirdparty/libopenui/src/coloredit.cpp create mode 100644 radio/src/thirdparty/libopenui/src/coloredit.h create mode 100644 radio/src/thirdparty/libopenui/src/dialog.cpp create mode 100644 radio/src/thirdparty/libopenui/src/dialog.h create mode 100644 radio/src/thirdparty/libopenui/src/filechoice.cpp create mode 100644 radio/src/thirdparty/libopenui/src/filechoice.h create mode 100644 radio/src/thirdparty/libopenui/src/font.h create mode 100644 radio/src/thirdparty/libopenui/src/form.cpp create mode 100644 radio/src/thirdparty/libopenui/src/form.h create mode 100644 radio/src/thirdparty/libopenui/src/gridlayout.h create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_base.h create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_number.cpp create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_number.h create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_text.cpp create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_text.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_defines.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_depends.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_file.cpp create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_file.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_globals.cpp create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_globals.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_helpers.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_types.h create mode 100644 radio/src/thirdparty/libopenui/src/mainwindow.cpp create mode 100644 radio/src/thirdparty/libopenui/src/mainwindow.h create mode 100644 radio/src/thirdparty/libopenui/src/menu.cpp create mode 100644 radio/src/thirdparty/libopenui/src/menu.h create mode 100644 radio/src/thirdparty/libopenui/src/menutoolbar.cpp create mode 100644 radio/src/thirdparty/libopenui/src/menutoolbar.h create mode 100644 radio/src/thirdparty/libopenui/src/numberedit.cpp create mode 100644 radio/src/thirdparty/libopenui/src/numberedit.h create mode 100644 radio/src/thirdparty/libopenui/src/page.cpp create mode 100644 radio/src/thirdparty/libopenui/src/page.h create mode 100644 radio/src/thirdparty/libopenui/src/progress.cpp create mode 100644 radio/src/thirdparty/libopenui/src/progress.h create mode 100644 radio/src/thirdparty/libopenui/src/slider.cpp create mode 100644 radio/src/thirdparty/libopenui/src/slider.h create mode 100644 radio/src/thirdparty/libopenui/src/static.cpp create mode 100644 radio/src/thirdparty/libopenui/src/static.h create mode 100644 radio/src/thirdparty/libopenui/src/tabsgroup.cpp create mode 100644 radio/src/thirdparty/libopenui/src/tabsgroup.h create mode 100644 radio/src/thirdparty/libopenui/src/textedit.cpp create mode 100644 radio/src/thirdparty/libopenui/src/textedit.h create mode 100644 radio/src/thirdparty/libopenui/src/theme.h create mode 100644 radio/src/thirdparty/libopenui/src/window.cpp create mode 100644 radio/src/thirdparty/libopenui/src/window.h diff --git a/radio/src/thirdparty/libopenui/CMakeLists.txt b/radio/src/thirdparty/libopenui/CMakeLists.txt new file mode 100644 index 000000000..5f0cf266a --- /dev/null +++ b/radio/src/thirdparty/libopenui/CMakeLists.txt @@ -0,0 +1 @@ +include(src) diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt new file mode 100644 index 000000000..752f93b90 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -0,0 +1,31 @@ +set(LIBOPENUI_SRC + libopenui_globals.cpp + libopenui_file.cpp + bitmapbuffer.cpp + window.cpp + form.cpp + button.cpp + static.cpp + checkbox.cpp + slider.cpp + mainwindow.cpp + menu.cpp + menutoolbar.cpp + choice.cpp + filechoice.cpp + numberedit.cpp + textedit.cpp + coloredit.cpp + progress.cpp + page.cpp + dialog.cpp + tabsgroup.cpp + ) + +if(HARDWARE_TOUCH) + set(LIBOPENUI_SRC + ${LIBOPENUI_SRC} + keyboard_text.cpp + keyboard_number.cpp + ) +endif() diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h new file mode 100644 index 000000000..7b8c9549f --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BASENUMBEREDIT_H_ +#define _BASENUMBEREDIT_H_ + +#include "form.h" + +class BaseNumberEdit : public FormField { + public: + BaseNumberEdit(Window * parent, const rect_t &rect, int32_t vmin, int32_t vmax, + std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)), + flags(flags) + { + } + + void setMin(int32_t value) + { + vmin = value; + } + + void setMax(int32_t value) + { + vmax = value; + } + + void setDefault(int32_t value) + { + vdefault = value; + } + + int32_t getMin() const + { + return vmin; + } + + int32_t getMax() const + { + return vmax; + } + + int32_t getDefault() const + { + return vdefault; + } + + void setStep(int32_t value) + { + step = value; + } + + int32_t getStep() const + { + return step; + } + + void setValue(int32_t value) + { + _setValue(limit(vmin, value, vmax)); + invalidate(); + } + + void setSetValueHandler(std::function handler) + { + _setValue = handler; + } + + int32_t getValue() + { + return _getValue(); + } + + protected: + int32_t vdefault = 0; + int32_t vmin; + int32_t vmax; + int32_t step = 1; + std::function _getValue; + std::function _setValue; + LcdFlags flags; +}; + +#endif // _BASENUMBEREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/bitfield.h b/radio/src/thirdparty/libopenui/src/bitfield.h new file mode 100644 index 000000000..abb309ab1 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/bitfield.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BITFIELD_H +#define _BITFIELD_H + +#include + +// A set of bitfield handling macros + +template +inline T bfBit(uint8_t n) +{ + return T(1) << n; +} + +#define BFBIT_SET(y, mask) ( y |= (mask) ) +#define BFBIT_CLEAR(y, mask) ( y &= ~(mask) ) +#define BFBIT_FLIP(y, mask) ( y ^= (mask) ) +#define BF_SINGLE_BIT_SET(y, i) BFBIT_SET(y, bfBit(i)) + +//! Create a bitmask of length 'len'. +template +inline T bfBitmask(uint8_t len) +{ + return bfBit(len) - 1; +} + +//! Create a bitfield mask of length 'len' starting at bit 'start'. +template +inline T bfMask(uint8_t start, uint8_t len) +{ + return bfBitmask(len) << start; +} + +//! Prepare a bitmask for insertion or combining. +template +inline T bfPrep(T x, uint8_t start, uint8_t len) +{ + return (x & bfBitmask(len)) << start; +} + +//! Extract a bitfield of length 'len' starting at bit 'start' from 'y'. +template +inline T bfGet(T y, uint8_t start, uint8_t len) +{ + return ((y)>>(start)) & bfBitmask(len); +} + +//! Insert 'len' bits of 'x 'into 'y', starting at bit 'start' from 'y'. +template +inline T bfSet(T to, T from, uint8_t start, uint8_t len) +{ + return (to & ~bfMask(start, len)) | bfPrep(from, start, len); +} + +template +inline T bfBitGet(T y, T mask) +{ + return (y & mask); +} + +template +inline T bfSingleBitGet(T y, uint8_t i) +{ + return bfBitGet(y, bfBit(i)); +} + +#endif //BITFIELD_H diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp new file mode 100644 index 000000000..accb21fd8 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -0,0 +1,925 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include +#include "bitmapbuffer.h" +#include "libopenui_depends.h" +#include "libopenui_globals.h" +#include "libopenui_helpers.h" +#include "libopenui_file.h" +#include "font.h" + +void BitmapBuffer::drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color) +{ + if (opacity == OPACITY_MAX) { + drawPixel(p, color); + } + else if (opacity != 0) { + uint8_t bgWeight = OPACITY_MAX - opacity; + RGB_SPLIT(color, red, green, blue); + RGB_SPLIT(*p, bgRed, bgGreen, bgBlue); + uint16_t r = (bgRed * bgWeight + red * opacity) / OPACITY_MAX; + uint16_t g = (bgGreen * bgWeight + green * opacity) / OPACITY_MAX; + uint16_t b = (bgBlue * bgWeight + blue * opacity) / OPACITY_MAX; + drawPixel(p, RGB_JOIN(r, g, b)); + } +} + +void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att) +{ + APPLY_OFFSET(); + + if (y >= height || y >= ymax || y < ymin) + return; + + if (x+w > width) { + w = width - x; + } + + pixel_t * p = getPixelPtr(x, y); + pixel_t color = lcdColorTable[COLOR_IDX(att)]; + uint8_t opacity = 0x0F - (att >> 24); + + if (pat == SOLID) { + while (w--) { + drawAlphaPixel(p, opacity, color); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + else { + while (w--) { + if (pat & 1) { + drawAlphaPixel(p, opacity, color); + pat = (pat >> 1) | 0x80; + } + else { + pat = pat >> 1; + } + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } +} + +void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags att) +{ + APPLY_OFFSET(); + + if (x < xmin || x >= xmax) + return; + + if (h < 0) { + y += h; + h = -h; + } + + if (y < ymin) { + h += y-ymin; + y = ymin; + } + + if (y + h > ymax) { + h = ymax - y; + } + + if (h <= 0) + return; + + pixel_t color = lcdColorTable[COLOR_IDX(att)]; + uint8_t opacity = 0x0F - (att >> 24); + + if (pat == SOLID) { + while (h--) { + drawAlphaPixel(x, y, opacity, color); + y++; + } + } + else { + if (pat==DOTTED && !(y%2)) { + pat = ~pat; + } + while (h--) { + if (pat & 1) { + drawAlphaPixel(x, y, opacity, color); + pat = (pat >> 1) | 0x80; + } + else { + pat = pat >> 1; + } + y++; + } + } +} + +void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att) +{ + for (int i=0; i= xmax || y >= ymax) + return; + + if (h < 0) { + y += h; + h = -h; + } + if (y < ymin) { + h += y-ymin; + y = ymin; + } + if (x < xmin) { + w += x - xmin; + x = xmin; + } + if (y + h > ymax) + h = ymax - y; + if (x + w > xmax) + w = xmax - x; + + if (!data || h<=0 || w<=0) + return; + + DMAFillRect(data, width, height, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]); +} + +void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att) +{ + APPLY_OFFSET(); + + if (x >= xmax || y >= ymax) + return; + + if (h < 0) { + y += h; + h = -h; + } + if (y < ymin) { + h += y-ymin; + y = ymin; + } + if (x < xmin) { + w += x - xmin; + x = xmin; + } + if (y + h > ymax) + h = ymax - y; + if (x + w > xmax) + w = xmax - x; + + for (coord_t i=y; i= 360 || endAngle <= 0) +// return false; +// +// if (startAngle == 0) { +// slopes[1] = 100000; +// slopes[2] = -100000; +// } +// else { +// float angle1 = float(startAngle) * (M_PI / 180.0f); +// if (startAngle >= 180) { +// slopes[1] = -100000; +// slopes[2] = cosf(angle1) * 100 / sinf(angle1); +// } +// else { +// slopes[1] = cosf(angle1) * 100 / sinf(angle1); +// slopes[2] = -100000; +// } +// } +// +// if (endAngle == 360) { +// slopes[0] = -100000; +// slopes[3] = 100000; +// } +// else { +// float angle2 = float(endAngle) * (M_PI / 180.0f); +// if (endAngle >= 180) { +// slopes[0] = -100000; +// slopes[3] = -cosf(angle2) * 100 / sinf(angle2); +// } +// else { +// slopes[0] = cosf(angle2) * 100 / sinf(angle2); +// slopes[3] = -100000; +// } +// } +// +// return true; +//} +// +//void BitmapBuffer::drawPie(int x0, int y0, int radius, int startAngle, int endAngle) +//{ +// int slopes[4]; +// if (!evalSlopes(slopes, startAngle, endAngle)) +// return; +// +// for (int y=0; y<=radius; y++) { +// for (int x=0; x<=radius; x++) { +// if (x*x+y*y <= radius*radius) { +// int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x); +// if (slope >= slopes[0] && slope < slopes[1]) { +// drawPixel(x0+x, y0-y, WHITE); +// } +// if (-slope >= slopes[0] && -slope < slopes[1]) { +// drawPixel(x0+x, y0+y, WHITE); +// } +// if (slope >= slopes[2] && slope < slopes[3]) { +// drawPixel(x0-x, y0-y, WHITE); +// } +// if (-slope >= slopes[2] && -slope < slopes[3]) { +// drawPixel(x0-x, y0+y, WHITE); +// } +// } +// } +// } +//} + +void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset, coord_t width) +{ + if (mask == nullptr) { + return; + } + + APPLY_OFFSET(); + + coord_t w = mask->getWidth(); + coord_t height = mask->getHeight(); + + if (!width || width > w) { + width = w; + } + + if (x + width > xmax) { + width = xmax - x; + } + + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + return; + } + + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + + for (coord_t row=0; row= ymax) + continue; + pixel_t * p = getPixelPtr(x, y+row); + const pixel_t * q = mask->getPixelPtr(offset, row); + for (coord_t col=0; col w) { + width = w; + } + + if (x + width > xmax) { + width = xmax - x; + } + + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + return; + } + + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + + for (coord_t row=0; row= ymax) + continue; + const uint8_t * q = bmp + 4 + row*w + offset; + for (coord_t col=0; col= xmin && xpixel < xmax) { + pixel_t * p = getPixelPtr(xpixel, ypixel); + if (p) + drawAlphaPixel(p, *q, color); + } + q++; + } + } +} + +uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags) +{ + coord_t offset = spec[index + 1]; + coord_t width = spec[index + 2] - offset; + if (width > 0) { + drawBitmapPattern(x, y, font, flags, offset, width); + } + return width; +} + +#define INCREMENT_POS(delta) do { if (flags & VERTICAL) y -= delta; else x += delta; } while(0) + +coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags) +{ + MOVE_OFFSET(); + + int height = getFontHeight(flags); + + if (y + height <= ymin || y >= ymax) { + RESTORE_OFFSET(); + return x; + } + + uint32_t fontindex = FONTINDEX(flags); + const unsigned char * font = fontsTable[fontindex]; + const uint16_t * fontspecs = fontspecsTable[fontindex]; + + if (flags & (RIGHT | CENTERED)) { + int width = getTextWidth(s, len, flags); + if (flags & RIGHT) { + INCREMENT_POS(-width); + } + else if (flags & CENTERED) { + INCREMENT_POS(-width / 2); + } + } + + coord_t & pos = (flags & VERTICAL) ? y : x; + const coord_t orig_pos = pos; + + while (len--) { + unsigned int c = uint8_t(*s); + + if (!c) { + break; + } + else if (c == 0x20) { + INCREMENT_POS(4); + } + else if (c >= 0xFE) { + s++; + c = uint8_t(*s) + ((c & 0x01) << 8) - 1; + if (c >= 0x101) + c -= 1; + c += 187; + uint8_t width = drawChar(x-1, y, font, fontspecs, c, flags); + INCREMENT_POS(width); + } + else if (c >= 0x20) { + uint8_t width = drawChar(x-1, y, font, fontspecs, getMappedChar(c), flags); + INCREMENT_POS(width); + } + else if (c == '\n') { + pos = orig_pos; + if (flags & VERTICAL) + x += height; + else + y += height; + } + + s++; + } + + RESTORE_OFFSET(); + + return pos - offsetX; +} + +void BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) +{ + char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix + char *s = str+32; + *s = '\0'; + int idx = 0; + int mode = MODE(flags); + bool neg = false; + if (val < 0) { + val = -val; + neg = true; + } + do { + *--s = '0' + (val % 10); + ++idx; + val /= 10; + if (mode!=0 && idx==mode) { + mode = 0; + *--s = '.'; + if (val==0) + *--s = '0'; + } + } while (val!=0 || mode>0 || (mode==MODE(LEADING0) && idxdrawSolidFilledRect(x, y, thickness, h, att); + dc->drawSolidFilledRect(x+w-thickness, y, thickness, h, att); + dc->drawSolidFilledRect(x, y, w, thickness, att); + dc->drawSolidFilledRect(x, y+h-thickness, w, thickness, att); +} + +//void BitmapBuffer::drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle) +//{ +// const uint16_t * q = img; +// coord_t width = *q++; +// coord_t height = *q++; +// +// int slopes[4]; +// if (!evalSlopes(slopes, startAngle, endAngle)) +// return; +// +// int w2 = width/2; +// int h2 = height/2; +// +// for (int y=h2-1; y>=0; y--) { +// for (int x=w2-1; x>=0; x--) { +// int slope = (x==0 ? 99000 : y*100/x); +// if (slope >= slopes[0] && slope < slopes[1]) { +// *getPixelPtr(x0+w2+x, y0+h2-y) = q[(h2-y)*width + w2+x]; +// } +// if (-slope >= slopes[0] && -slope < slopes[1]) { +// *getPixelPtr(x0+w2+x, y0+h2+y) = q[(h2+y)*width + w2+x]; +// } +// if (slope >= slopes[2] && slope < slopes[3]) { +// *getPixelPtr(x0+w2-x, y0+h2-y) = q[(h2-y)*width + w2-x]; +// } +// if (-slope >= slopes[2] && -slope < slopes[3]) { +// *getPixelPtr(x0+w2-x, y0+h2+y) = q[(h2+y)*width + w2-x]; +// } +// } +// } +//} +// +//void BitmapBuffer::drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle) +//{ +// coord_t width = *((uint16_t *)img); +// coord_t height = *(((uint16_t *)img)+1); +// const uint8_t * q = img+4; +// +// int slopes[4]; +// if (!evalSlopes(slopes, startAngle, endAngle)) +// return; +// +// pixel_t color = lcdColorTable[COLOR_IDX(flags)]; +// +// int w2 = width/2; +// int h2 = height/2; +// +// for (int y=h2-1; y>=0; y--) { +// for (int x=w2-1; x>=0; x--) { +// int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x); +// if (slope >= slopes[0] && slope < slopes[1]) { +// drawAlphaPixel(x0+w2+x, y0+h2-y, q[(h2-y)*width + w2+x], color); +// } +// if (-slope >= slopes[0] && -slope < slopes[1]) { +// drawAlphaPixel(x0+w2+x, y0+h2+y, q[(h2+y)*width + w2+x], color); +// } +// if (slope >= slopes[2] && slope < slopes[3]) { +// drawAlphaPixel(x0+w2-x, y0+h2-y, q[(h2-y)*width + w2-x], color); +// } +// if (-slope >= slopes[2] && -slope < slopes[3]) { +// drawAlphaPixel(x0+w2-x, y0+h2+y, q[(h2+y)*width + w2-x], color); +// } +// } +// } +//} + +BitmapBuffer * BitmapBuffer::load(const char * filename) +{ + const char * ext = getFileExtension(filename); + if (ext && !strcmp(ext, ".bmp")) + return load_bmp(filename); + else + return load_stb(filename); +} + +BitmapBuffer * BitmapBuffer::loadMask(const char * filename) +{ + BitmapBuffer * bitmap = BitmapBuffer::load(filename); + if (bitmap) { + pixel_t * p = bitmap->getPixelPtr(0, 0); + for (int i = bitmap->getWidth() * bitmap->getHeight(); i > 0; i--) { + *((uint8_t *)p) = OPACITY_MAX - ((*p) >> 12); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + return bitmap; +} + +BitmapBuffer * BitmapBuffer::loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background) +{ + BitmapBuffer * result = nullptr; + BitmapBuffer * mask = BitmapBuffer::loadMask(filename); + if (mask) { + result = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); + if (result) { + result->clear(background); + result->drawMask(0, 0, mask, foreground); + } + delete mask; + } + return result; +} + +FIL imgFile __DMA; + +BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) +{ + UINT read; + uint8_t palette[16]; + uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */ + uint8_t * buf = &bmpBuf[0]; + + FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); + if (result != FR_OK) { + return nullptr; + } + + if (f_size(&imgFile) < 14) { + f_close(&imgFile); + return nullptr; + } + + result = f_read(&imgFile, buf, 14, &read); + if (result != FR_OK || read != 14) { + f_close(&imgFile); + return nullptr; + } + + if (buf[0] != 'B' || buf[1] != 'M') { + f_close(&imgFile); + return nullptr; + } + + uint32_t fsize = *((uint32_t *)&buf[2]); + uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */ + + uint32_t len = limit((uint32_t)4, (uint32_t)(hsize-14), (uint32_t)32); + result = f_read(&imgFile, buf, len, &read); + if (result != FR_OK || read != len) { + f_close(&imgFile); + return nullptr; + } + + uint32_t ihsize = *((uint32_t *)&buf[0]); /* more header size */ + + /* invalid header size */ + if (ihsize + 14 > hsize) { + f_close(&imgFile); + return nullptr; + } + + /* sometimes file size is set to some headers size, set a real size in that case */ + if (fsize == 14 || fsize == ihsize + 14) + fsize = f_size(&imgFile) - 2; + + /* declared file size less than header size */ + if (fsize <= hsize) { + f_close(&imgFile); + return nullptr; + } + + uint32_t w, h; + + switch (ihsize){ + case 40: // windib + case 56: // windib v3 + case 64: // OS/2 v2 + case 108: // windib v4 + case 124: // windib v5 + w = *((uint32_t *)&buf[4]); + h = *((uint32_t *)&buf[8]); + buf += 12; + break; + case 12: // OS/2 v1 + w = *((uint16_t *)&buf[4]); + h = *((uint16_t *)&buf[6]); + buf += 8; + break; + default: + f_close(&imgFile); + return nullptr; + } + + if (*((uint16_t *)&buf[0]) != 1) { /* planes */ + f_close(&imgFile); + return nullptr; + } + + uint16_t depth = *((uint16_t *)&buf[2]); + + buf = &bmpBuf[0]; + + if (depth == 4) { + if (f_lseek(&imgFile, hsize-64) != FR_OK || f_read(&imgFile, buf, 64, &read) != FR_OK || read != 64) { + f_close(&imgFile); + return nullptr; + } + for (uint8_t i=0; i<16; i++) { + palette[i] = buf[4*i]; + } + } + else { + if (f_lseek(&imgFile, hsize) != FR_OK) { + f_close(&imgFile); + return nullptr; + } + } + + BitmapBuffer * bmp = new BitmapBuffer(BMP_RGB565, w, h); + if (bmp == nullptr || bmp->getData() == nullptr) { + f_close(&imgFile); + return nullptr; + } + + uint32_t rowSize; + bool hasAlpha = false; + + switch (depth) { + case 32: + for (int i=h-1; i>=0; i--) { + pixel_t * dst = bmp->getPixelPtr(0, i); + for (unsigned int j=0; j> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + } + else { + if ((pixel & 0xff) == 0xff) { + *dst = RGB(pixel >> 24, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + } + else { + hasAlpha = true; + bmp->setFormat(BMP_ARGB4444); + for (pixel_t * p = bmp->getPixelPtr(j, i); p != bmp->getPixelPtr(0, h); MOVE_TO_NEXT_RIGHT_PIXEL(p)) { + pixel_t tmp = *p; + *p = ((tmp >> 1) & 0x0f) + (((tmp >> 7) & 0x0f) << 4) + (((tmp >> 12) & 0x0f) << 8); + } + *dst = ARGB(pixel & 0xff, (pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + } + } + MOVE_TO_NEXT_RIGHT_PIXEL(dst); + } + } + break; + + case 1: + break; + + case 4: + rowSize = ((4*w+31)/32)*4; + for (int32_t i=h-1; i>=0; i--) { + result = f_read(&imgFile, buf, rowSize, &read); + if (result != FR_OK || read != rowSize) { + f_close(&imgFile); + delete bmp; + return nullptr; + } + pixel_t * dst = bmp->getPixelPtr(0, i); + for (uint32_t j=0; j> ((j & 1) ? 0 : 4)) & 0x0F; + uint8_t val = palette[index]; + *dst = RGB(val, val, val); + MOVE_TO_NEXT_RIGHT_PIXEL(dst); + } + } + break; + + default: + f_close(&imgFile); + delete bmp; + return nullptr; + } + + f_close(&imgFile); + return bmp; +} + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_ONLY_JPEG +#define STBI_ONLY_BMP +#define STBI_ONLY_GIF +#define STBI_NO_STDIO +#define STBI_NO_HDR +#define STBI_NO_LINEAR + +// #define TRACE_STB_MALLOC + +#if defined(TRACE_STB_MALLOC) +#define STBI_MALLOC(sz) stb_malloc(sz) +#define STBI_REALLOC_SIZED(p,oldsz,newsz) stb_realloc(p,oldsz,newsz) +#define STBI_FREE(p) stb_free(p) + +void * stb_malloc(unsigned int size) +{ + void * res = malloc(size); + TRACE("malloc %d = %p", size, res); + return res; +} + +void stb_free(void *ptr) +{ + TRACE("free %p", ptr); + free(ptr); +} + +void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) +{ + void * res = realloc(ptr, newsz); + TRACE("realloc %p, %d -> %d = %p", ptr, oldsz, newsz, res); + return res; +} +#endif // #if defined(TRACE_STB_MALLOC) + +#include "thirdparty/Stb/stb_image.h" + +// fill 'data' with 'size' bytes. return number of bytes actually read +int stbc_read(void *user, char *data, int size) +{ + FIL * fp = (FIL *)user; + UINT br = 0; + FRESULT res = f_read(fp, data, size, &br); + if (res == FR_OK) { + return (int)br; + } + return 0; +} + +// skip the next 'n' bytes, or 'unget' the last -n bytes if negative +void stbc_skip(void *user, int n) +{ + FIL * fp = (FIL *)user; + f_lseek(fp, f_tell(fp) + n); +} + +// returns nonzero if we are at end of file/data +int stbc_eof(void *user) +{ + FIL * fp = (FIL *)user; + int res = f_eof(fp); + return res; +} + +// callbacks for stb-image +const stbi_io_callbacks stbCallbacks = { + stbc_read, + stbc_skip, + stbc_eof +}; + +BitmapBuffer * BitmapBuffer::load_stb(const char * filename) +{ + FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); + if (result != FR_OK) { + return nullptr; + } + + int w, h, n; + unsigned char * img = stbi_load_from_callbacks(&stbCallbacks, &imgFile, &w, &h, &n, 4); + f_close(&imgFile); + + if (!img) { + return nullptr; + } + + // convert to RGB565 or ARGB4444 format + BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h); + if (bmp == NULL) { + TRACE("load_stb() malloc failed"); + stbi_image_free(img); + return nullptr; + } + +#if 0 + DMABitmapConvert(bmp->data, img, w, h, n == 4 ? DMA2D_ARGB4444 : DMA2D_RGB565); +#else + pixel_t * dest = bmp->getPixelPtr(0, 0); + const uint8_t * p = img; + if (n == 4) { + for (int row = 0; row < h; ++row) { + for (int col = 0; col < w; ++col) { + *dest = ARGB(p[3], p[0], p[1], p[2]); + MOVE_TO_NEXT_RIGHT_PIXEL(dest); + p += 4; + } + } + } + else { + for (int row = 0; row < h; ++row) { + for (int col = 0; col < w; ++col) { + *dest = RGB(p[0], p[1], p[2]); + MOVE_TO_NEXT_RIGHT_PIXEL(dest); + p += 4; + } + } + } +#endif + + stbi_image_free(img); + return bmp; +} diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h new file mode 100644 index 000000000..5a6631168 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BITMAP_BUFFER_H_ +#define _BITMAP_BUFFER_H_ + +#include +#include +#include +#include "libopenui_types.h" +#include "libopenui_defines.h" +#include "libopenui_depends.h" +#include "debug.h" + +constexpr uint8_t SOLID = 0xFF; +constexpr uint8_t DOTTED = 0x55; +constexpr uint8_t STASHED = 0x33; + +#define MOVE_OFFSET() coord_t offsetX = this->offsetX; x += offsetX; this->offsetX = 0; coord_t offsetY = this->offsetY; y += offsetY; this->offsetY = 0 +#define APPLY_OFFSET() x += offsetX; y += offsetY +#define RESTORE_OFFSET() this->offsetX = offsetX, this->offsetY = offsetY + +#if defined(PCBX10) && !defined(SIMU) + #define MOVE_PIXEL_RIGHT(p, count) p -= count +#else + #define MOVE_PIXEL_RIGHT(p, count) p += count +#endif + +#define MOVE_TO_NEXT_RIGHT_PIXEL(p) MOVE_PIXEL_RIGHT(p, 1) + +#define USE_STB + +enum BitmapFormats +{ + BMP_RGB565, + BMP_ARGB4444 +}; + +template +class BitmapBufferBase +{ + public: + BitmapBufferBase(uint8_t format, uint16_t width, uint16_t height, T * data): + format(format), + width(width), + height(height), + xmax(width), + ymax(height), + data(data), + data_end(data + (width * height)) + { + } + + BitmapBufferBase(uint8_t format, T * data): + format(format), + width(*((uint16_t*)data)), + height(*(((uint16_t*)data) + 1)), + xmax(width), + ymax(height), + data(data + 2), + data_end(data + 2 + (width * height)) + { + } + + inline void clearClippingRect() + { + xmin = 0; + xmax = width; + ymin = 0; + ymax = height; + } + + inline void setClippingRect(coord_t xmin, coord_t xmax, coord_t ymin, coord_t ymax) + { + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + } + + inline void getClippingRect(coord_t & xmin, coord_t & xmax, coord_t & ymin, coord_t & ymax) + { + xmin = this->xmin; + xmax = this->xmax; + ymin = this->ymin; + ymax = this->ymax; + } + + inline void setOffset(coord_t offsetX, coord_t offsetY) + { + this->offsetX = offsetX; + this->offsetY = offsetY; + } + + coord_t getOffsetX() const + { + return offsetX; + } + + coord_t getOffsetY() const + { + return offsetY; + } + + inline uint8_t getFormat() const + { + return format; + } + + inline uint16_t getWidth() const + { + return width; + } + + inline uint16_t getHeight() const + { + return height; + } + + inline T * getData() const + { + return data; + } + + uint32_t getDataSize() const + { + return width * height * sizeof(T); + } + + inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const + { +#if defined(PCBX10) && !defined(SIMU) + x = width - x - 1; + y = height - y - 1; +#endif + return &data[y*width + x]; + } + + protected: + uint8_t format; + coord_t width; + coord_t height; + coord_t xmin = 0; + coord_t xmax; + coord_t ymin = 0; + coord_t ymax; + coord_t offsetX = 0; + coord_t offsetY = 0; + T * data; + T * data_end; +}; + +typedef BitmapBufferBase Bitmap; + +class RLEBitmap: + public BitmapBufferBase +{ + public: + RLEBitmap(uint8_t format, const uint8_t* rle_data) : + BitmapBufferBase(format, 0, 0, nullptr) + { + width = *((uint16_t *)rle_data); + height = *(((uint16_t *)rle_data)+1); + uint32_t pixels = width * height; + data = (uint16_t*)malloc(pixels * sizeof(uint16_t)); + decode((uint8_t *)data, pixels * sizeof(uint16_t), rle_data+4); + data_end = data + pixels; + } + + ~RLEBitmap() + { + free(data); + } + + static int decode(uint8_t * dest, unsigned int dest_size, const unsigned char* src) + { + //unsigned char count = 0; + unsigned char prev_byte = 0; + bool prev_byte_valid = false; + + const unsigned char * dest_end = dest + dest_size; + unsigned char * d = dest; + + while (d < dest_end) { + *d++ = *src; + if (prev_byte_valid && (*src == prev_byte)) { + src++; + + if (d + *src > dest + dest_size) { +// TRACE("rle_decode_8bit: destination overflow!\n"); + return -1; + } + + memset(d, prev_byte, *src); + d += *src++; + prev_byte_valid = false; + } + else { + prev_byte = *src++; + prev_byte_valid = true; + } + } + + return d - dest; + } +}; + +class BitmapBuffer: public BitmapBufferBase +{ + private: + bool dataAllocated; +#if defined(DEBUG) + bool leakReported; +#endif + + public: + + BitmapBuffer(uint8_t format, uint16_t width, uint16_t height): + BitmapBufferBase(format, width, height, nullptr), + dataAllocated(true) +#if defined(DEBUG) + , leakReported(false) +#endif + { + data = (uint16_t *)malloc(width*height*sizeof(uint16_t)); + data_end = data + (width * height); + } + + BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, uint16_t * data): + BitmapBufferBase(format, width, height, data), + dataAllocated(false) +#if defined(DEBUG) + , leakReported(false) +#endif + { + } + + ~BitmapBuffer() + { + if (dataAllocated) { + free(data); + } + } + + inline void setFormat(uint8_t format) + { + this->format = format; + } + + inline void clear(LcdFlags flags=0) + { + drawSolidFilledRect(0, 0, width - offsetX, height - offsetY, flags); + } + + inline void drawPixel(pixel_t * p, pixel_t value) + { + if (data && (data <= p || p < data_end)) { + *p = value; + } +#if defined(DEBUG) + else if (!leakReported) { + leakReported = true; + TRACE("BitmapBuffer(%p).drawPixel(): buffer overrun, data: %p, written at: %p", this, data, p); + } +#endif + } + + inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const + { +#if defined(PCBX10) && !defined(SIMU) + x = width - x - 1; + y = height - y - 1; +#endif + return &data[y*width + x]; + } + + inline pixel_t * getPixelPtr(coord_t x, coord_t y) + { +#if defined(PCBX10) && !defined(SIMU) + x = width - x - 1; + y = height - y - 1; +#endif + return &data[y*width + x]; + } + + inline void drawPixel(coord_t x, coord_t y, pixel_t value) + { + pixel_t * p = getPixelPtr(x, y); + drawPixel(p, value); + } + + void drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color); + + inline void drawAlphaPixel(coord_t x, coord_t y, uint8_t opacity, uint16_t color) + { + pixel_t * p = getPixelPtr(x, y); + drawAlphaPixel(p, opacity, color); + } + + void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0); + + void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0); + + inline void drawSolidHorizontalLine(coord_t x, coord_t y, coord_t w, LcdFlags flags) + { + drawSolidFilledRect(x, y, w, 1, flags); + } + + inline void drawSolidVerticalLine(coord_t x, coord_t y, coord_t h, LcdFlags flags) + { + drawSolidFilledRect(x, y, 1, h, flags); + } + + void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, uint8_t pat = SOLID, LcdFlags flags = 0); + + inline void drawSolidRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, LcdFlags flags = 0) + { + drawSolidFilledRect(x, y, thickness, h, flags); + drawSolidFilledRect(x+w-thickness, y, thickness, h, flags); + drawSolidFilledRect(x, y, w, thickness, flags); + drawSolidFilledRect(x, y+h-thickness, w, thickness, flags); + } + + void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); + + void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0); + + void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); + + void drawCircle(int x0, int y0, int radius); + + void drawPie(int x0, int y0, int radius, int startAngle, int endAngle); + + void drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle); + + void drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle); + + static BitmapBuffer * load(const char * filename); + + static BitmapBuffer * loadMask(const char * filename); + + static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background); + + void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset=0, coord_t width=0); + + void drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset=0, coord_t width=0); + + coord_t drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags=0); + + void drawText(coord_t x, coord_t y, const char * s, LcdFlags flags = 0) + { + drawSizedText(x, y, s, 255, flags); + } + + void drawTextAtIndex(coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags = 0) + { + char length = *s++; + drawSizedText(x, y, s+length*idx, length, flags); + } + + void drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); + + template + void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) + { + APPLY_OFFSET(); + + if (x >= xmax) return; + if (y >= ymax) return; + + if (!data || !bmp) + return; + + coord_t srcw = bmp->getWidth(); + coord_t srch = bmp->getHeight(); + + if (w == 0) + w = srcw; + if (h == 0) + h = srch; + if (srcx + w > srcw) + w = srcw - srcx; + if (srcy + h > srch) + h = srch - srcy; + + if (x < xmin) { + w += x-xmin; + srcx -= x-xmin; + x = xmin; + } + + if (y < ymin) { + h += y-ymin; + srcy -= y-ymin; + y = ymin; + } + + if (x + w > xmax) { + w = xmax - x; + } + + if (y + h > ymax) { + h = ymax - y; + } + + if (w <= 0 || h <= 0) + return; + + // TRACE("x=%d xmin=%d xmax=%d w=%d", x, xmin, xmax, w); + + if (scale == 0) { + /*if (x + w > width) { + w = width - x; + } + if (y + h > height) { + h = height - y; + }*/ + if (bmp->getFormat() == BMP_ARGB4444) { + DMACopyAlphaBitmap(data, width, height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + } + else { + DMACopyBitmap(data, width, height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + } + } + else { + int scaledw = w * scale; + int scaledh = h * scale; + + if (x + scaledw > width) + scaledw = width - x; + if (y + scaledh > height) + scaledh = height - y; + + for (int i = 0; i < scaledh; i++) { + pixel_t * p = getPixelPtr(x, y + i); + const pixel_t * qstart = bmp->getPixelPtr(srcx, srcy + int(i / scale)); + for (int j = 0; j < scaledw; j++) { + const pixel_t * q = qstart; + MOVE_PIXEL_RIGHT(q, int(j / scale)); + if (bmp->getFormat() == BMP_ARGB4444) { + ARGB_SPLIT(*q, a, r, g, b); + drawAlphaPixel(p, a, RGB_JOIN(r<<1, g<<2, b<<1)); + } + else { + drawPixel(p, *q); + } + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + } + } + + template + void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h) + { + float vscale = float(h) / bitmap->getHeight(); + float hscale = float(w) / bitmap->getWidth(); + float scale = vscale < hscale ? vscale : hscale; + + int xshift = (w - (bitmap->getWidth() * scale)) / 2; + int yshift = (h - (bitmap->getHeight() * scale)) / 2; + drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); + } + + protected: + static BitmapBuffer * load_bmp(const char * filename); + static BitmapBuffer * load_stb(const char * filename); + uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); +}; + +extern BitmapBuffer * lcd; + +#endif // _BITMAP_BUFFER_H_ diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp new file mode 100644 index 000000000..84b33b2db --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "libopenui_config.h" +#include "button.h" +#include "font.h" +#include "theme.h" + +void Button::onPress() +{ + bool check = (pressHandler && pressHandler()); + if (check != bool(flags & BUTTON_CHECKED)) { + flags ^= BUTTON_CHECKED; + invalidate(); + } +} + +#if defined(HARDWARE_KEYS) +void Button::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onPress(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Button::onTouchEnd(coord_t x, coord_t y) +{ + if (enabled()) { + onPress(); + if (!(flags & BUTTON_NOFOCUS)) { + setFocus(); + } + } + return true; +} +#endif + +void Button::checkEvents() +{ + Window::checkEvents(); + if (checkHandler) + checkHandler(); +} + +void TextButton::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor = TEXT_COLOR; + + if (checked()) { + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, TEXT_INVERTED_BGCOLOR); + } + else { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, TEXT_INVERTED_BGCOLOR); + } + textColor = TEXT_INVERTED_COLOR; + } + else { + if (flags & BUTTON_BACKGROUND) { + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, CURVE_AXIS_COLOR); + } + else { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + } + } + else { + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } + } + } + + dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | textColor); +} + +void IconButton::paint(BitmapBuffer * dc) +{ + dc->drawBitmap(0, 0, theme->getIcon(icon, checked() ? STATE_PRESSED : STATE_DEFAULT)); +} + + +const uint8_t __alpha_button_on[] { +#include "alpha_button_on.lbm" +}; +Bitmap ALPHA_BUTTON_ON(BMP_ARGB4444, (const uint16_t*)__alpha_button_on); + +const uint8_t __alpha_button_off[] { +#include "alpha_button_off.lbm" +}; +Bitmap ALPHA_BUTTON_OFF(BMP_ARGB4444, (const uint16_t*)__alpha_button_off); + + +FabIconButton::FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags): + Button(parent, { x - 34, y - 34, 68, 68 }, pressHandler, flags), + icon(icon) +{ +} + +void FabIconButton::paint(BitmapBuffer * dc) +{ + dc->drawBitmap(0, 0, checked() ? &ALPHA_BUTTON_ON : &ALPHA_BUTTON_OFF); + const BitmapBuffer * mask = theme->getIconMask(icon); + if (mask) { + dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, TEXT_BGCOLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h new file mode 100644 index 000000000..c72910107 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BUTTON_H_ +#define _BUTTON_H_ + +#include "form.h" +#include + +enum ButtonFlags { + BUTTON_BACKGROUND = 1, + BUTTON_CHECKED = 2, + BUTTON_NOFOCUS = 4, + BUTTON_CHECKED_ON_FOCUS = 8, + BUTTON_DISABLED = 16, +}; + +class Button : public FormField { + public: + Button(Window * parent, const rect_t & rect, std::function pressHandler=nullptr, uint8_t flags=0): + FormField(parent, rect), + pressHandler(pressHandler), + flags(flags) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Button"; + } +#endif + + void enable(bool enabled=true) + { + if (!enabled != bool(flags & BUTTON_DISABLED)) { + flags ^= BUTTON_DISABLED; + invalidate(); + } + } + + void disable() + { + enable(false); + } + + void check(bool checked=true) + { + if (checked != bool(flags & BUTTON_CHECKED)) { + flags ^= BUTTON_CHECKED; + invalidate(); + } + } + + bool enabled() + { + return !(flags & BUTTON_DISABLED); + } + + bool checked() + { + if (flags & BUTTON_CHECKED_ON_FOCUS) + return hasFocus(); + else + return flags & BUTTON_CHECKED; + } + + void setPressHandler(std::function handler) + { + pressHandler = std::move(handler); + } + + void setCheckHandler(std::function handler) + { + checkHandler = std::move(handler); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event); +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void checkEvents() override; + + protected: + std::function pressHandler; + std::function checkHandler; + uint8_t flags; + + void onPress(); +}; + +class TextButton : public Button { + public: + TextButton(Window * parent, const rect_t & rect, std::string text, std::function pressHandler=nullptr, uint8_t flags=BUTTON_BACKGROUND): + Button(parent, rect, pressHandler, flags), + text(std::move(text)) + { + windowFlags = OPAQUE; + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextButton \"" + text + "\""; + } +#endif + + void setText(std::string value) + { + if (value != text) { + text = std::move(value); + invalidate(); + } + } + + void paint(BitmapBuffer * dc) override; + + protected: + std::string text; +}; + +class IconButton: public Button { + public: + IconButton(Window * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, uint8_t flags = 0): + Button(parent, rect, pressHandler, flags), + icon(icon) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "IconButton(" + std::to_string(icon) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +}; + +class FabIconButton: public Button { + public: + FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags=0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FabIconButton(" + std::to_string(icon) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp new file mode 100644 index 000000000..9b0da61d5 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "checkbox.h" +#include "theme.h" + +void CheckBox::paint(BitmapBuffer * dc) +{ + theme->drawCheckBox(dc, getValue(), hasFocus()); +} + +#if defined(HARDWARE_KEYS) +void CheckBox::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + setValue(!getValue()); + invalidate({0, 0, 20, 20}); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool CheckBox::onTouchEnd(coord_t x, coord_t y) +{ + setValue(!getValue()); + setFocus(); + invalidate({0, 0, 20, 20}); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h new file mode 100644 index 000000000..0e199400f --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _CHECKBOX_H_ +#define _CHECKBOX_H_ + +#include "form.h" + +class CheckBox : public FormField { + public: + CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, + LcdFlags flags = 0) : + FormField(parent, rect), + getValue(getValue), + setValue(setValue) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "CheckBox"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + const char * label; + std::function getValue; + std::function setValue; +}; + +#endif // _CHECKBOX_H_ diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp new file mode 100644 index 000000000..2c3fe383f --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "choice.h" +#include "menu.h" + +const uint8_t LBM_DROPDOWN[] = { +#include "mask_dropdown.lbm" +}; + +Choice::Choice(Window * parent, const rect_t &rect, const char * values, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, LcdFlags flags) : + FormField(parent, rect), + values(values), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)), + flags(flags) +{ +} + +void Choice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else + textColor = 0; + + if (textHandler) + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, textHandler(getValue()).c_str(), textColor); + else + dc->drawTextAtIndex(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, values, getValue() - vmin, flags | textColor); + + dc->drawBitmapPattern(rect.w - 14, (rect.h - 5) / 2, LBM_DROPDOWN, textColor); +} + +#if defined(HARDWARE_KEYS) +void Choice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +void Choice::openMenu() +{ + auto menu = new Menu(); + auto value = getValue(); + int count = 0; + int current = -1; + + for (int i = vmin; i <= vmax; ++i) { + if (isValueAvailable && !isValueAvailable(i)) + continue; + if (textHandler) { + menu->addLine(textHandler(i), [=]() { + setValue(i); + }); + } + else { + menu->addLine(TEXT_AT_INDEX(values, i - vmin), [=]() { + setValue(i); + }); + } + if (value == i) { + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } + + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); +} + +#if defined(HARDWARE_TOUCH) +bool Choice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h new file mode 100644 index 000000000..2f644a6cc --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _CHOICE_H_ +#define _CHOICE_H_ + +#include +#include "form.h" + +class Choice : public FormField { + public: + Choice(Window * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Choice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void setSetValueHandler(std::function handler) + { + setValue = std::move(handler); + } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setTextHandler(std::function handler) + { + textHandler = std::move(handler); + } + + protected: + const char * values; + int16_t vmin; + int16_t vmax; + std::function getValue; + std::function setValue; + std::function isValueAvailable; + std::function textHandler; + LcdFlags flags; + void openMenu(); +}; + +#endif // _CHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp new file mode 100644 index 000000000..326d79fc0 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "coloredit.h" +#include "numberedit.h" +#include "bitfield.h" + +constexpr uint8_t PART_BITS[3][2] = { {0, 5}, {5, 6}, {11, 5} }; + +class ColorBox: public Window { + public: + ColorBox(Window * parent, const rect_t & rect, LcdFlags color): + Window(parent, rect), + color(color) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ColorBox"; + } +#endif + + void paint(BitmapBuffer * dc) override + { + lcdSetColor(color); + dc->drawSolidFilledRect(0, 0, width(), height(), TEXT_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, CUSTOM_COLOR); + } + + void setColor(LcdFlags value) + { + color = value; + invalidate(); + } + + protected: + LcdFlags color; +}; + +ColorEdit::ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue): + Window(parent, rect, FORWARD_SCROLL) +{ + auto width = rect.w / 4 - 5; + + // The color box + auto box = new ColorBox(this, {0, 0, rect.w - 3 * width, rect.h}, getValue()); + + // The 3 parts of RGB + for (uint8_t part = 0; part < 3; part++) { + new NumberEdit(this, {rect.w - (3 - part) * width + 5, 0, width - 5, rect.h}, 0, (1 << PART_BITS[part][1]) - 1, + [=]() { + return bfGet(getValue(), PART_BITS[part][0], PART_BITS[part][1]); + }, + [=](uint16_t newValue) { + uint16_t value = getValue(); + value = bfSet(value, newValue, PART_BITS[part][0], PART_BITS[part][1]); + setValue(value); + box->setColor(value); + }); + } + +} + +ColorEdit::~ColorEdit() +{ + deleteChildren(); +} diff --git a/radio/src/thirdparty/libopenui/src/coloredit.h b/radio/src/thirdparty/libopenui/src/coloredit.h new file mode 100644 index 000000000..e78d91785 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/coloredit.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _COLOREDIT_H_ +#define _COLOREDIT_H_ + +#include "window.h" + +class ColorEdit : public Window { + public: + ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue = nullptr); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ColorEdit"; + } +#endif + + ~ColorEdit() override; +}; + +#endif // _COLOREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp new file mode 100644 index 000000000..9bef8a708 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "dialog.h" +#include "mainwindow.h" +#include "form.h" + +Dialog::Dialog(std::string title, const rect_t rect): + Window(&mainWindow, rect, OPAQUE), + title(std::move(title)), + previousFocus(focusWindow) +{ + bringToTop(); + FormField::clearCurrentField(); + setFocus(); +} + +void Dialog::paint(BitmapBuffer * dc) +{ + dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, TEXT_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, TITLE_BGCOLOR); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), MENU_TITLE_COLOR); + dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, TEXT_BGCOLOR_INDEX); +} + +void Dialog::deleteLater() +{ + if (previousFocus) { + previousFocus->setFocus(); + } + + Window::deleteLater(); +} diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h new file mode 100644 index 000000000..5ec3bf8aa --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _DIALOG_H_ +#define _DIALOG_H_ + +#include "window.h" + +class Dialog: public Window { + public: + Dialog(std::string title, const rect_t rect); + + void paint(BitmapBuffer * dc) override; + + void deleteLater(); + + protected: + std::string title; + Window * previousFocus; +}; + +#endif // _CONFIRMATION_H_ diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp new file mode 100644 index 000000000..2dc4fd580 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "filechoice.h" +#include "libopenui_file.h" +#include "menu.h" + +const uint8_t LBM_FOLDER[] = { +#include "mask_folder.lbm" +}; + +// comparison, not case sensitive. +bool compare_nocase(const std::string &first, const std::string &second) +{ + return strcasecmp(first.c_str(), second.c_str()) < 0; +} + +FileChoice::FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): + FormField(parent, rect), + folder(std::move(folder)), + extension(extension), + maxlen(maxlen), + getValue(std::move(getValue)), + setValue(std::move(setValue)) +{ +} + +void FileChoice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + const char * displayedValue = getValue().c_str(); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (displayedValue[0] == '\0') + textColor = CURVE_AXIS_COLOR; + else + textColor = TEXT_COLOR; + + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue[0] == '\0' ? "---" : displayedValue, textColor); + dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_FOLDER, textColor); +} + +void FileChoice::openMenu() +{ + FILINFO fno; + DIR dir; + std::list files; + const char * fnExt; + uint8_t fnLen, extLen; + + FRESULT res = f_opendir(&dir, folder.c_str()); // Open the directory + if (res == FR_OK) { + bool firstTime = true; + for (;;) { + res = sdReadDir(&dir, &fno, firstTime); + if (res != FR_OK || fno.fname[0] == 0) + break; // break on error or end of dir + if (fno.fattrib & AM_DIR) + continue; // skip subfolders + if (fno.fattrib & AM_HID) + continue; // skip hidden files + if (fno.fattrib & AM_SYS) + continue; // skip system files + + fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); + fnLen -= extLen; + + if (!fnLen || fnLen > maxlen) + continue; // wrong size + if (extension && !isExtensionMatching(fnExt, extension)) + continue; // wrong extension + + files.emplace_back(fno.fname, fnLen); + } + + if (files.size() > 0) { + // sort files + files.sort(compare_nocase); + + files.push_front(""); + + auto menu = new Menu(); + int count = 0; + int current = -1; + std::string value = getValue(); + for (const auto &file: files) { + menu->addLine(file, [=]() { + setValue(file); + }); + // TRACE("%s %d %s %d", value.c_str(), value.size(), file.c_str(), file.size()); + if (value.compare(file) == 0) { + // TRACE("OK"); + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } + + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); + } + else { + TRACE("NO FILES !!!!!"); + // TODO popup "NO FILES" + } + } +} + +#if defined(HARDWARE_KEYS) +void FileChoice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool FileChoice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h new file mode 100644 index 000000000..7c5b1f958 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _FILECHOICE_H_ +#define _FILECHOICE_H_ + +#include "form.h" +#include + +class FileChoice: public FormField { + public: + FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FileChoice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + std::string folder; + const char * extension; + int maxlen; + std::function getValue; + std::function setValue; + + void openMenu(); +}; + +#endif // _FILECHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/font.h b/radio/src/thirdparty/libopenui/src/font.h new file mode 100644 index 000000000..6503e3c78 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/font.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _FONT_H_ +#define _FONT_H_ + +#include "libopenui_types.h" + +uint8_t getMappedChar(uint8_t c); +uint8_t getFontHeight(LcdFlags flags); +int getCharWidth(uint8_t c, const uint16_t * spec); +int getTextWidth(const char * s, int len = 0, LcdFlags flags = 0); + +#endif // _FONT_H_ diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp new file mode 100644 index 000000000..96712696e --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "form.h" +#include "bitmapbuffer.h" +#include "libopenui_config.h" + +FormField * FormField::current = nullptr; + +FormField::FormField(Window *parent, const rect_t &rect, uint8_t flags) : + Window(parent, rect, flags) +{ + if (current) { + setPreviousField(current); + current->setNextField(this); + } + + FormWindow * form = dynamic_cast(parent); + if (form && !form->getFirstField()) + form->setFirstField(this); + + current = this; +} + +#if defined(HARDWARE_KEYS) +void FormWindow::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_EXIT) && first && getFocus() != first) { + first->setFocus(); + } + else { + Window::onKeyEvent(event); + } +} + +void FormField::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { + if (next) { + next->setFocus(); + } + } + else if (event == EVT_ROTARY_LEFT/*EVT_KEY_BREAK(KEY_UP)*/) { + if (previous) { + previous->setFocus(); + } + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = !editMode; + invalidate(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + editMode = false; + invalidate(); + } + else { + Window::onKeyEvent(event); + } +} + +void FormGroup::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + first->setFocus(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + editMode = false; + setFocus(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +void FormField::paint(BitmapBuffer * dc) +{ + if (editMode) { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, TEXT_INVERTED_BGCOLOR); + } + else if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } +} + +void FormGroup::paint(BitmapBuffer * dc) +{ + if (!editMode && hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h new file mode 100644 index 000000000..c6087d5d4 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -0,0 +1,197 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _FORM_H_ +#define _FORM_H_ + +#include "window.h" + +class FormField: public Window { + friend class FormWindow; + + public: + FormField(Window * parent, const rect_t & rect, uint8_t flags=0); + + inline void setNextField(FormField *field) + { + next = field; + } + + inline void setPreviousField(FormField * field) + { + previous = field; + } + + inline static void setCurrentField(FormField * field) + { + current = field; + } + + inline static void clearCurrentField() + { + setCurrentField(nullptr); + }; + + inline static FormField * getCurrentField() + { + return current; + }; + + inline FormField * getPreviousField() + { + return previous; + }; + + inline FormField * getNextField() + { + return next; + }; + + inline static void link(FormField * previous, FormField * next) + { + previous->setNextField(next); + next->setPreviousField(previous); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + protected: + static FormField * current; + FormField * next = nullptr; + FormField * previous = nullptr; + bool editMode = false; +}; + +class FormGroup: public FormField { + public: + FormGroup(Window * parent, const rect_t & rect, uint8_t flags=0) : + FormField(parent, rect, flags) + { + FormField::current = nullptr; + } + + ~FormGroup() + { + deleteChildren(); + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FormGroup"; + } +#endif + + void clear() + { + Window::clear(); + first = nullptr; + FormField::current = nullptr; + } + + inline void setFirstField(FormField * field) + { + first = field; + } + + inline void setLastField(FormField * field = nullptr) + { + // by default the current field is the last one + if (!field) + field = getCurrentField(); + + FormField::link(field, first); + + // now the current field is the group, it will be linked to the next one + FormField::setCurrentField(this); + } + + FormField * getFirstField() + { + return first; + } + + protected: + FormField * first = nullptr; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override; +}; + +class FormWindow: public Window { + public: + FormWindow(Window * parent, const rect_t & rect) : + Window(parent, rect) + { + FormField::current = nullptr; + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FormWindow"; + } +#endif + + void clear() + { + first = nullptr; + Window::clear(); + } + + FormField * getFirstField() + { + return first; + } + + inline void setFirstField(FormField * field) + { + first = field; + field->setFocus(); + } + + inline void setLastField(FormField * field = nullptr) + { + if (!field) + field = FormField::getCurrentField(); + FormField::link(field, first); + } + + protected: + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override + { + } + + protected: + FormField * first = nullptr; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/gridlayout.h b/radio/src/thirdparty/libopenui/src/gridlayout.h new file mode 100644 index 000000000..9134b7c72 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/gridlayout.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _GRIDLAYOUT_H_ +#define _GRIDLAYOUT_H_ + +#include "window.h" + +class GridLayout { + public: + GridLayout(coord_t width): + width(width) + { + } + + GridLayout(Window * window): + width(window->width()) + { + } + + rect_t getSlot(uint8_t count = 1, uint8_t index = 0) const + { + coord_t width = (this->width - (count - 1) * PAGE_LINE_SPACING) / count; + coord_t left = (width + PAGE_LINE_SPACING) * index; + return {left, currentY, width, PAGE_LINE_HEIGHT}; + } + + void spacer(coord_t height=PAGE_LINE_SPACING) + { + currentY += height; + } + + void nextLine(coord_t height=PAGE_LINE_HEIGHT) + { + spacer(height + PAGE_LINE_SPACING); + } + + void addWindow(Window * window) + { + window->adjustHeight(); + currentY += window->rect.h + PAGE_LINE_SPACING; + } + + coord_t getWindowHeight() const + { + return currentY; + } + + protected: + coord_t width; + coord_t currentY = 0; +}; + +class FormGridLayout: public GridLayout { + public: + FormGridLayout(coord_t width = LCD_W): + GridLayout(width) + { + } + + void setLabelWidth(coord_t value) + { + labelWidth = value; + } + + void setMarginLeft(coord_t value) + { + lineMarginLeft = value; + } + + void setMarginRight(coord_t value) + { + lineMarginRight = value; + } + + rect_t getLineSlot() + { + return { lineMarginLeft, currentY, this->width - lineMarginRight - lineMarginLeft, PAGE_LINE_HEIGHT }; + } + + rect_t getCenteredSlot(coord_t width = 0 ) const + { + if (width == 0) + width = this->width; + return { lineMarginLeft + (this->width - width)/2, currentY, min(width, this->width - lineMarginRight - lineMarginLeft), PAGE_LINE_HEIGHT }; + } + + rect_t getLabelSlot(bool indent = false) const + { + coord_t left = indent ? lineMarginLeft + PAGE_INDENT_WIDTH : lineMarginLeft; + return { left, currentY, labelWidth - left, PAGE_LINE_HEIGHT }; + } + + rect_t getFieldSlot(uint8_t count = 1, uint8_t index = 0) const + { + coord_t width = (this->width - labelWidth - lineMarginRight - (count - 1) * PAGE_LINE_SPACING) / count; + coord_t left = labelWidth + (width + PAGE_LINE_SPACING) * index; + return {left, currentY, width, PAGE_LINE_HEIGHT}; + } + + protected: + coord_t labelWidth = PAGE_LABEL_WIDTH; + coord_t lineMarginLeft = PAGE_PADDING; + coord_t lineMarginRight = 10; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h new file mode 100644 index 000000000..3fdbf0d5a --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _KEYBOARD_BASE_H_ +#define _KEYBOARD_BASE_H_ + +#include "mainwindow.h" +#include "libopenui_types.h" + +class Page; +class TabsGroup; + +template +class Keyboard: public Window { + public: + explicit Keyboard(coord_t height): + Window(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) + { + } + + void setField(T * newField) + { + field = newField; + attach(&mainWindow); + Window * w = getPageBody(); + w->setHeight(LCD_H - height() - w->top()); + w->scrollTo(newField); + invalidate(); + } + + void disable(bool resizePage) + { + detach(); + if (field) { + Window * w = getPageBody(); + w->setHeight(LCD_H - 0 - w->top()); + field = nullptr; + } + } + + T * getField() const + { + return field; + } + + protected: + T * field = nullptr; + Window * getPageBody() + { + Window * parent = field; + while (true) { + Window * tmp = parent->getParent(); + if ((tmp->getWindowFlags() & OPAQUE) && tmp->width() == LCD_W && tmp->height() == LCD_H) { + return parent; + } + parent = tmp; + } + } +}; + +#endif // _KEYBOARD_BASE_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp new file mode 100644 index 000000000..ead6f270b --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "keyboard_number.h" +#include "button.h" +#include "basenumberedit.h" + +constexpr coord_t KEYBOARD_HEIGHT = 90; + +NumberKeyboard * NumberKeyboard::_instance = nullptr; + +NumberKeyboard::NumberKeyboard(): + Keyboard(KEYBOARD_HEIGHT) +{ + new TextButton(this, { LCD_W/2 - 115, 10, 50, 30 }, "<<", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() - 10 * field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 55, 10, 50, 30 }, "-", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() - field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 5, 10, 50, 30 }, "+", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() + field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 65, 10, 50, 30 }, ">>", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() + 10 * field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 115, 50, 50, 30 }, "MIN", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getMin()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 65, 50, 50, 30 }, "MAX", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getMax()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 55, 50, 110, 30 }, "DEFAULT", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getDefault()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); +} + +NumberKeyboard::~NumberKeyboard() +{ + _instance = nullptr; +} + +void NumberKeyboard::paint(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); +} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h new file mode 100644 index 000000000..2d6cf63e0 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _KEYBOARD_NUMBER_H_ +#define _KEYBOARD_NUMBER_H_ + +#include "keyboard_base.h" +#include "basenumberedit.h" + +class NumberKeyboard : public Keyboard { + friend class BaseNumberEdit; + + public: + NumberKeyboard(); + + ~NumberKeyboard() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "NumberKeyboard"; + } +#endif + + static NumberKeyboard * instance() + { + if (!_instance) + _instance = new NumberKeyboard(); + return _instance; + } + + void paint(BitmapBuffer * dc) override; + + protected: + static NumberKeyboard * _instance; +}; + +#endif // _KEYBOARD_NUMBER_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp new file mode 100644 index 000000000..378e95758 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "keyboard_text.h" +#include "libopenui_globals.h" +#include "textedit.h" +#include "font.h" + +constexpr coord_t KEYBOARD_HEIGHT = 160; + +TextKeyboard * TextKeyboard::_instance = nullptr; + +const uint8_t LBM_KEY_UPPERCASE[] = { +#include "mask_key_uppercase.lbm" +}; + +const uint8_t LBM_KEY_LOWERCASE[] = { +#include "mask_key_lowercase.lbm" +}; + +const uint8_t LBM_KEY_BACKSPACE[] = { +#include "mask_key_backspace.lbm" +}; + +const uint8_t LBM_KEY_LETTERS[] = { +#include "mask_key_letters.lbm" +}; + +const uint8_t LBM_KEY_NUMBERS[] = { +#include "mask_key_numbers.lbm" +}; + +const uint8_t LBM_KEY_SPACEBAR[] = { +#include "mask_key_spacebar.lbm" +}; + +const uint8_t * const LBM_SPECIAL_KEYS[] = { + LBM_KEY_BACKSPACE, + LBM_KEY_UPPERCASE, + LBM_KEY_LOWERCASE, + LBM_KEY_LETTERS, + LBM_KEY_NUMBERS, +}; + +const char * const KEYBOARD_LOWERCASE[] = { + "qwertyuiop", + " asdfghjkl", + "\201zxcvbnm\200", + "\204\t\n" +}; + +const char * const KEYBOARD_UPPERCASE[] = { + "QWERTYUIOP", + " ASDFGHJKL", + "\202ZXCVBNM\200", + "\204\t\n" +}; + +const char * const KEYBOARD_NUMBERS[] = { + "1234567890", + "_-", + " \200", + "\203\t\n" +}; + +const char * const * const KEYBOARD_LAYOUTS[] = { + KEYBOARD_UPPERCASE, + KEYBOARD_LOWERCASE, + KEYBOARD_LOWERCASE, + KEYBOARD_NUMBERS, +}; + +TextKeyboard::TextKeyboard(): + Keyboard(KEYBOARD_HEIGHT), + layout(KEYBOARD_LOWERCASE) +{ +} + +TextKeyboard::~TextKeyboard() +{ + _instance = nullptr; +} + +void TextKeyboard::setCursorPos(coord_t x) +{ + if (!field) + return; + + uint8_t size = field->getMaxLength(); + char * data = field->getData(); + coord_t rest = x; + for (cursorIndex = 0; cursorIndex < size; cursorIndex++) { + if (data[cursorIndex] == '\0') + break; + char c = data[cursorIndex]; + uint8_t w = getCharWidth(c, fontspecsTable[0]); + if (rest < w) + break; + rest -= w; + } + cursorPos = x - rest; + field->invalidate(); +} + +void TextKeyboard::paint(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); + + for (uint8_t i=0; i<4; i++) { + coord_t y = 15 + i * 40; + coord_t x = 15; + const char * c = layout[i]; + while(*c) { + if (*c == ' ') { + x += 15; + } + else if (*c == '\t') { + // spacebar + dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, TEXT_COLOR); + x += 135; + } + else if (*c == '\n') { + // enter + dc->drawSolidFilledRect(x, y-2, 80, 25, TEXT_DISABLE_COLOR); + dc->drawText(x+40, y, "ENTER", CENTERED); + x += 80; + } + else if (int8_t(*c) < 0) { + dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], TEXT_COLOR); + x += 45; + } + else { + dc->drawSizedText(x, y, c, 1); + x += 30; + } + c++; + } + } +} + +bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) +{ + if (!field) + return false; + + uint8_t size = field->getMaxLength(); + char * data = field->getData(); + + char c = 0; + + uint8_t row = max(0, y - 5) / 40; + const char * key = layout[row]; + while(*key) { + if (*key == ' ') { + x -= 15; + } + else if (*key == '\t') { + if (x <= 135) { + c = ' '; + break; + } + x -= 135; + } + else if (*key == '\n') { + if (x <= 80) { + // enter + disable(true); + return true; + } + x -= 80; + } + else if (int8_t(*key) < 0) { + if (x <= 45) { + uint8_t specialKey = *key; + if (specialKey == 128) { + // backspace + if (cursorIndex > 0) { + char c = data[cursorIndex - 1]; + memmove(data + cursorIndex - 1, data + cursorIndex, size - cursorIndex); + data[size - 1] = '\0'; + cursorPos -= getCharWidth(c, fontspecsTable[0]); + --cursorIndex; + } + } + else { + layout = KEYBOARD_LAYOUTS[specialKey - 129]; + invalidate(); + } + break; + } + x -= 45; + } + else { + if (x <= 30) { + c = *key; + break; + } + x -= 30; + } + key++; + } + + if (c && size - cursorIndex > 1) { + memmove(data + cursorIndex + 1, data + cursorIndex, size - cursorIndex - 1); + data[cursorIndex++] = c; + cursorPos += getCharWidth(c, fontspecsTable[0]); + } + + field->invalidate(); + return true; +} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h new file mode 100644 index 000000000..36daf8672 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _KEYBOARD_TEXT_H_ +#define _KEYBOARD_TEXT_H_ + +#include "keyboard_base.h" +#include "textedit.h" + +class TextKeyboard : public Keyboard { + friend class TextEdit; + + public: + TextKeyboard(); + + ~TextKeyboard() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextKeyboard"; + } +#endif + + static TextKeyboard * instance() { + if (!_instance) + _instance = new TextKeyboard(); + return _instance; + } + + coord_t getCursorPos() const + { + return cursorPos; + } + + void setCursorPos(coord_t x); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + static TextKeyboard * _instance; + coord_t cursorPos = 0; + uint8_t cursorIndex = 0; + const char * const * layout; +}; + +#endif // _KEYBOARD_TEXT_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h new file mode 100644 index 000000000..a65f59d36 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_DEFINES_H_ +#define _LIBOPEUI_DEFINES_H_ + +#include "libopenui_types.h" + +/* lcd common flags */ +#define BLINK 0x01 + +/* drawText flags */ +#define INVERS 0x02 +#define LEFT 0x00 /* align left */ +#define CENTERED 0x04 /* align center */ +#define RIGHT 0x08 /* align right */ +#define SHADOWED 0x80 /* black copy at +1 +1 */ + +/* drawNumber flags */ +#define LEADING0 0x10 +#define PREC1 0x20 +#define PREC2 0x30 +#define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4) + +/* rect, square flags */ +#define ROUND 0x04 + +/* telemetry flags */ +#define NO_UNIT 0x40 + +enum FontSizeIndex +{ + STDSIZE_INDEX, + TINSIZE_INDEX, + SMLSIZE_INDEX, + MIDSIZE_INDEX, + DBLSIZE_INDEX, + XXLSIZE_INDEX, + SPARE6_INDEX, + SPARE7_INDEX, + STDSIZE_BOLD_INDEX, + SPARE9_INDEX, + SPAREa_INDEX, + SPAREb_INDEX, + SPAREc_INDEX, + SPAREd_INDEX, + SPAREe_INDEX, + SPAREf_INDEX, +}; + +#define STDSIZE (STDSIZE_INDEX << 8) +#define TINSIZE (TINSIZE_INDEX << 8) +#define SMLSIZE (SMLSIZE_INDEX << 8) +#define MIDSIZE (MIDSIZE_INDEX << 8) +#define DBLSIZE (DBLSIZE_INDEX << 8) +#define XXLSIZE (XXLSIZE_INDEX << 8) +#define BOLD (STDSIZE_BOLD_INDEX << 8) +#define FONTSIZE_MASK 0x0f00 + +#if !defined(BOOT) +#define FONTSIZE(flags) ((flags) & FONTSIZE_MASK) +#define FONTINDEX(flags) (FONTSIZE(flags) >> 8) +#else +#define FONTSIZE(flags) STDSIZE +#define FONTINDEX(flags) STDSIZE_INDEX +#endif + +#define TIMEBLINK 0x1000 +#define TIMEHOUR 0x2000 +#define EXPANDED 0x2000 +#define VERTICAL 0x4000 + +#define ARGB_SPLIT(color, a, r, g, b) \ + uint16_t a = ((color) & 0xF000) >> 12; \ + uint16_t r = ((color) & 0x0F00) >> 8; \ + uint16_t g = ((color) & 0x00F0) >> 4; \ + uint16_t b = ((color) & 0x000F) + +#define RGB_SPLIT(color, r, g, b) \ + uint16_t r = ((color) & 0xF800) >> 11; \ + uint16_t g = ((color) & 0x07E0) >> 5; \ + uint16_t b = ((color) & 0x001F) + +#define RGB_JOIN(r, g, b) \ + (((r) << 11) + ((g) << 5) + (b)) + +#define GET_RED(color) \ + (((color) & 0xF800) >> 8) + +#define GET_GREEN(color) \ + (((color) & 0x07E0) >> 3) + +#define GET_BLUE(color) \ + (((color) & 0x001F) << 3) + +// remove windows default definitions +#undef OPAQUE +#undef RGB + +#define OPACITY_MAX 0x0F +#define OPACITY(x) ((x)<<24) + +#define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3)) +#define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) + +#define COLOR(index) LcdFlags((index) << 16) +#define COLOR_IDX(att) uint8_t((att) >> 16) + +#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h new file mode 100644 index 000000000..e2c529126 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_DEPENDS_H_ +#define _LIBOPEUI_DEPENDS_H_ + +#include +#include "libopenui_config.h" + +void lcdNextLayer(); +void lcdSetColor(uint16_t color); +void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void onKeyPress(); +void onKeyError(); + +#endif // _LIBOPEUI_DEPENDS_H_ diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp new file mode 100644 index 000000000..d01a1db3c --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include +#include +#include "libopenui_file.h" + +const char * getFileExtension(const char * filename, uint8_t size, uint8_t extMaxLen, uint8_t * fnlen, uint8_t * extlen) +{ + int len = size; + if (!size) { + len = strlen(filename); + } + if (!extMaxLen) { + extMaxLen = LEN_FILE_EXTENSION_MAX; + } + if (fnlen != nullptr) { + *fnlen = (uint8_t)len; + } + for (int i=len-1; i >= 0 && len-i <= extMaxLen; --i) { + if (filename[i] == '.') { + if (extlen) { + *extlen = len-i; + } + return &filename[i]; + } + } + if (extlen != nullptr) { + *extlen = 0; + } + return nullptr; +} + +// returns true if current working dir is at the root level +bool isCwdAtRoot() +{ + char path[10]; + if (f_getcwd(path, sizeof(path)-1) == FR_OK) { + return (strcasecmp("/", path) == 0); + } + return false; +} + +/* + Wrapper around the f_readdir() function which + also returns ".." entry for sub-dirs. (FatFS 0.12 does + not return ".", ".." dirs anymore) +*/ +FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime) +{ + FRESULT res; + if (firstTime && !isCwdAtRoot()) { + // fake parent directory entry + strcpy(fno->fname, ".."); + fno->fattrib = AM_DIR; + res = FR_OK; + } + else { + res = f_readdir(dir, fno); /* Read a directory item */ + } + firstTime = false; + return res; +} + diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h new file mode 100644 index 000000000..0aca1a8cf --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_FILE_H_ +#define _LIBOPEUI_FILE_H_ + +#include +#include "ff.h" + +constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. + +const char * getFileExtension(const char * filename, uint8_t size = 0, uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); +bool isExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); +FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime); + +#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp b/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp new file mode 100644 index 000000000..90cefa0ba --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "libopenui_globals.h" + +uint16_t lcdColorTable[LCD_COLOR_COUNT]; + diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h new file mode 100644 index 000000000..b4900e5ac --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_GLOBALS_H_ +#define _LIBOPEUI_GLOBALS_H_ + +#include +#include "libopenui_config.h" + +extern uint16_t lcdColorTable[LCD_COLOR_COUNT]; +extern const uint16_t * const fontspecsTable[FONT_TABLE_SIZE]; +extern const uint8_t * fontsTable[FONT_TABLE_SIZE]; + +#endif // _LIBOPEUI_GLOBALS_H_ diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp b/radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp new file mode 100644 index 000000000..196771132 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "libopenui_helpers.h" + diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h new file mode 100644 index 000000000..ed70ddd07 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_HELPERS_H_ +#define _LIBOPEUI_HELPERS_H_ + +#include + +template +inline T min(T a, T b) +{ + return a < b ? a : b; +} + +template +inline T max(T a, T b) +{ + return a > b ? a : b; +} + +template +inline T limit(T vmin, T x, T vmax) +{ + return min(max(vmin, x), vmax); +} + +inline int divRoundClosest(const int n, const int d) +{ + if (d == 0) + return 0; + else + return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d); +} + +inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) +{ + const char * start = val + 1 + idx * val[0]; + return std::string(start, min(val[0], strlen(start))); +} + +#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h new file mode 100644 index 000000000..5cf077897 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_TYPES_H_ +#define _LIBOPEUI_TYPES_H_ + +#include + +typedef uint16_t pixel_t; + +typedef int coord_t; + +struct point_t +{ + coord_t x; + coord_t y; +}; + +struct rect_t +{ + coord_t x, y, w, h; + + constexpr coord_t left() const + { + return x; + } + + constexpr coord_t right() const + { + return x + w; + } + + constexpr coord_t top() const + { + return y; + } + + constexpr coord_t bottom() const + { + return y + h; + } +}; + +typedef uint32_t LcdFlags; +typedef uint16_t event_t; + +#endif // _LIBOPEUI_TYPES_H_ diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp new file mode 100644 index 000000000..a0fe22406 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "mainwindow.h" + +#if defined(HARDWARE_TOUCH) +#include "touch.h" +#endif + +MainWindow mainWindow; + +#if defined(HARDWARE_TOUCH) +STRUCT_TOUCH touchState; +#endif + +void MainWindow::emptyTrash() +{ + for (auto window: trash) { + delete window; + } + trash.clear(); +} + +void MainWindow::checkEvents(bool trash) +{ +#if defined(HARDWARE_TOUCH) + if (touchState.Event == TE_DOWN) { + onTouchStart(touchState.X + scrollPositionX, touchState.Y + scrollPositionY); + // touchState.Event = TE_NONE; + } + else if (touchState.Event == TE_UP) { + touchState.Event = TE_NONE; + onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + } + else if (touchState.Event == TE_SLIDE) { + coord_t x = touchState.X - touchState.lastX; + coord_t y = touchState.Y - touchState.lastY; + onTouchSlide(touchState.X, touchState.Y, touchState.startX, touchState.startY, x, y); + touchState.lastX = touchState.X; + touchState.lastY = touchState.Y; + } +#endif + + Window::checkEvents(); + + if (trash) { + emptyTrash(); + } +} + +void MainWindow::invalidate(const rect_t & rect) +{ + if (invalidatedRect.w) { + coord_t left = min(invalidatedRect.left(), rect.left()); + coord_t right = max(invalidatedRect.right(), rect.right()); + coord_t top = min(invalidatedRect.top(), rect.top()); + coord_t bottom = max(invalidatedRect.bottom(), rect.bottom()); + invalidatedRect = {left, top, right - left, bottom - top}; + } + else { + invalidatedRect = rect; + } +} + +bool MainWindow::refresh() +{ + if (invalidatedRect.w) { + if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { + TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); + BitmapBuffer * previous = lcd; + lcdNextLayer(); + lcdCopy(lcd->getData(), previous->getData()); + } + else { + TRACE_WINDOWS("Refresh full screen"); + lcdNextLayer(); + } + lcd->setOffset(0, 0); + lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); + fullPaint(lcd); + invalidatedRect.w = 0; + return true; + } + else { + return false; + } +} + +void MainWindow::run(bool trash) +{ + checkEvents(trash); + if (refresh()) { + lcdRefresh(); + } +} diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h new file mode 100644 index 000000000..31bae69df --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ + +#include +#include "window.h" +#include "bitmapbuffer.h" + +class MainWindow: public Window { + public: + MainWindow(): + Window(nullptr, {0, 0, LCD_W, LCD_H}), + invalidatedRect(rect) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MainWindow"; + } +#endif + + void checkEvents(bool trash=true); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void invalidate(const rect_t & rect) override; + + bool refresh(); + + void run(bool trash=true); + + protected: + void emptyTrash(); + rect_t invalidatedRect; + const char * shutdown = nullptr; +}; + +extern MainWindow mainWindow; + +#endif // _MAINWINDOW_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp new file mode 100644 index 000000000..f4c1bf3d8 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "menu.h" + +MenuWindow::MenuWindow(Menu * parent): + Window(parent, {LCD_W / 2 - 100, LCD_H / 2, 200, 0}, OPAQUE) +{ + setFocus(); +} + +void MenuWindow::select(int index) +{ + selectedIndex = index; + if (innerHeight > height()) { + setScrollPositionY(lineHeight * index - 3 * lineHeight); + } + invalidate(); +} + +#if defined(HARDWARE_KEYS) +void MenuWindow::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT) { + select((selectedIndex + 1) % lines.size()); + } + else if (event == EVT_ROTARY_LEFT) { + select(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1); + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + Window::onKeyEvent(event); // the window above will be closed on event + lines[selectedIndex].onPress(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + if (onCancel) + onCancel(); + Window::onKeyEvent(event); + } + else { + Window::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool MenuWindow::onTouchEnd(coord_t x, coord_t y) +{ + int index = y / lineHeight; + lines[index].onPress(); + return false; // = close the menu (inverted so that click outside the menu closes it) +} +#endif + +void MenuWindow::paint(BitmapBuffer * dc) +{ + int width = (innerHeight > height() ? 195 : 200); + dc->clear(HEADER_BGCOLOR); + for (unsigned i = 0; i < lines.size(); i++) { + auto & line = lines[i]; + LcdFlags flags = (selectedIndex == (int) i ? WARNING_COLOR : MENU_TITLE_COLOR); + if (line.drawLine) { + line.drawLine(dc, 0, i * lineHeight /*+ (lineHeight - 20) / 2*/, flags); + } + else { + const char *text = line.text.data(); + dc->drawText(10, i * lineHeight + (lineHeight - 20) / 2, text[0] == '\0' ? "---" : text, flags); + } + if (i > 0) { + dc->drawSolidHorizontalLine(0, i * lineHeight - 1, width, CURVE_AXIS_COLOR); + } + } +} + +void Menu::updatePosition() +{ + if (!toolbar) { + // there is no navigation bar at the left, we may center the window on screen + int count = min(MenuWindow::maxLines, menuWindow.lines.size()); + coord_t h = count * MenuWindow::lineHeight - 1; + menuWindow.setTop((LCD_H - h) / 2 + 20); + menuWindow.setHeight(h); + } + menuWindow.setInnerHeight(menuWindow.lines.size() * MenuWindow::lineHeight - 1); +} + +void Menu::addLine(const std::string & text, std::function onPress) +{ + menuWindow.addLine(text, std::move(onPress)); + updatePosition(); +} + +void Menu::addLine(std::function drawLine, std::function onPress) +{ + menuWindow.addLine(drawLine, std::move(onPress)); + updatePosition(); +} + +void Menu::removeLines() +{ + menuWindow.removeLines(); + updatePosition(); +} + +#if defined(HARDWARE_KEYS) +void Menu::onKeyEvent(event_t event) +{ + if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { + toolbar->onKeyEvent(event); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { + deleteLater(); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Menu::onTouchEnd(coord_t x, coord_t y) +{ + if (!Window::onTouchEnd(x, y)) { + deleteLater(); + } + return true; +} + +bool Menu::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h new file mode 100644 index 000000000..25e7f7184 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MENU_H_ +#define _MENU_H_ + +#include +#include +#include +#include +#include "mainwindow.h" + +class Menu; + +class MenuWindow: public Window { + friend class Menu; + + class MenuLine { + friend class MenuWindow; + + public: + MenuLine(std::string text, std::function onPress): + text(std::move(text)), + onPress(std::move(onPress)) + { + } + + MenuLine(std::function drawLine, std::function onPress): + drawLine(std::move(drawLine)), + onPress(std::move(onPress)) + { + } + + MenuLine(MenuLine &) = delete; + + MenuLine(MenuLine &&) = default; + + protected: + std::string text; + std::function drawLine; + std::function onPress; + }; + + public: + MenuWindow(Menu * parent); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MenuWindow"; + } +#endif + + void setCancelHandler(std::function handler) + { + onCancel = handler; + } + + void addLine(const std::string & text, std::function onPress) + { + lines.emplace_back(text, onPress); + invalidate(); + } + + void addLine(std::function drawLine, std::function onPress) + { + lines.emplace_back(drawLine, onPress); + invalidate(); + } + + void removeLines() + { + lines.clear(); + invalidate(); + } + + void select(int index); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + std::vector lines; +#if defined(HARDWARE_TOUCH) + int selectedIndex = -1; +#else + int selectedIndex = 0; +#endif + std::function onCancel; + static constexpr uint8_t maxLines = 7; // TODO NV14 was 8 + static constexpr uint8_t lineHeight = 30; // TODO NV14 was 40 +}; + +class Menu : public Window { + public: + Menu() : + Window(&mainWindow, {0, 0, LCD_W, LCD_H}, TRANSPARENT), +#if !defined(HARDWARE_TOUCH) + previousFocus(focusWindow), +#endif + menuWindow(this) + { + } + + ~Menu() override + { + delete toolbar; + } + + void setCancelHandler(std::function handler) + { + menuWindow.setCancelHandler(handler); + } + + void deleteLater() + { +#if !defined(HARDWARE_TOUCH) + if (previousFocus) { + previousFocus->setFocus(); + } +#endif + Window::deleteLater(); + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Menu"; + } +#endif + + void setToolbar(Window * window) + { + toolbar = window; + menuWindow.setLeft(toolbar->right()); + menuWindow.setTop(toolbar->top()); + menuWindow.setHeight(toolbar->height()); + } + + void addLine(const std::string & text, std::function onPress); + + void addLine(std::function drawLine, std::function onPress); + + void removeLines(); + + void select(int index) + { + menuWindow.select(index); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override + { + return true; + } + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; +#endif + + protected: + Window * previousFocus; + MenuWindow menuWindow; + Window * toolbar = nullptr; + void updatePosition(); +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp new file mode 100644 index 000000000..4834d56ac --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "menutoolbar.h" +#include "font.h" + +void MenuToolbarButton::paint(BitmapBuffer * dc) +{ + if (checked()) { + dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, HEADER_BGCOLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | MENU_TITLE_COLOR); + } + else { + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | TEXT_COLOR); + } +} \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h new file mode 100644 index 000000000..4ca1de84c --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MENUTOOLBAR_H_ +#define _MENUTOOLBAR_H_ + +#include "button.h" +#include "menu.h" + +int getFirstAvailable(int min, int max, std::functionisValueAvailable); + +class MenuToolbarButton: public Button { + public: + MenuToolbarButton(Window * window, const rect_t & rect, char picto): +#if defined(HARDWARE_TOUCH) + Button(window, rect, nullptr, BUTTON_CHECKED_ON_FOCUS), +#else + Button(window, rect, nullptr), +#endif + picto(picto) + { + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override + { + if (hasFocus()) { + check(false); + clearFocus(); + } + else { + setFocus(); + } + onPress(); + return true; + } +#endif + + void check(bool checked=true) + { + Button::check(checked); + onPress(); + Button::check(checked); + } + + protected: + char picto; +}; + +template +class MenuToolbar: public Window { + friend T; + + public: + MenuToolbar(T * choice, Menu * menu): + Window(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR), + choice(choice), + menu(menu) + { + FormField::clearCurrentField(); + } + + ~MenuToolbar() override + { + deleteChildren(); + } + + void paint(BitmapBuffer * dc) override + { + dc->clear(CURVE_AXIS_COLOR); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override + { + if (event == EVT_KEY_BREAK(KEY_PGDN)) { + if (current == children.end()) { + current = children.begin(); + } + else { + static_cast(*current)->check(false); + ++current; + } + if (current != children.end()) { + auto button = static_cast(*current); + button->check(true); + scrollTo(button); + } + else { + setScrollPositionY(0); + } + } + else if (event == EVT_KEY_LONG(KEY_PGDN)) { + killEvents(event); + if (current == children.end()) { + --current; + } + else { + static_cast(*current)->check(false); + if (current == children.begin()) + current = children.end(); + else + --current; + } + if (current != children.end()) { + auto button = static_cast(*current); + button->check(true); + scrollTo(button); + } + else { + setScrollPositionY(0); + } + } + } +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override + { + Window::onTouchEnd(x, y); + return true; // = don't close the menu (inverted so that click outside the menu closes it) + } +#endif + + protected: + std::list::iterator current = children.end(); + T * choice; + Menu * menu; + coord_t y = 0; // TODO NV14 was 5; + + void addButton(char picto, int16_t filtermin, int16_t filtermax) + { + int vmin = choice->vmin; + int vmax = choice->vmax; + + if (vmin > filtermin || vmax < filtermin) + return; + + if (choice->isValueAvailable && getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0) + return; + + auto button = new MenuToolbarButton(this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}, picto); + button->setPressHandler([=]() { + if (button->checked()) { + choice->fillMenu(menu, [=](int16_t index) { + return index >= filtermin && index <= filtermax; + }); + } + else { + choice->fillMenu(menu); + } + return 1; + }); + + y += 30; + + setInnerHeight(y); + } +}; + +#endif // _MENUTOOLBAR_H_ + diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp new file mode 100644 index 000000000..d268d8513 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "numberedit.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_number.h" +#endif + +NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, LcdFlags flags): + BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), flags) +{ +} + +void NumberEdit::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (enabled) + textColor = TEXT_COLOR; + else + textColor = CURVE_AXIS_COLOR; + + int32_t value = getValue(); + if (displayFunction) + displayFunction(dc, textColor, value); + else if (value == 0 && !zeroText.empty()) + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | flags); + else + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | flags, 0, prefix.c_str(), suffix.c_str()); +} + +#if defined(HARDWARE_KEYS) +void NumberEdit::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + if (event == EVT_ROTARY_RIGHT) { + int value = getValue(); + do { + value += rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); + if (value <= vmax) + setValue(value); + else + onKeyError(); + return; + } + else if (event == EVT_ROTARY_LEFT) { + int value = getValue(); + do { + value -= rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); + if (value >= vmin) + setValue(value); + else + onKeyError(); + return; + } + } + + FormField::onKeyEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool NumberEdit::onTouchEnd(coord_t, coord_t) +{ + if (!enabled) { + return true; + } + + if (!hasFocus()) { + setFocus(); + } + + NumberKeyboard * keyboard = NumberKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + return true; +} +#endif + +void NumberEdit::onFocusLost() +{ +#if defined(HARDWARE_TOUCH) + NumberKeyboard::instance()->disable(true); +#endif + + FormField::onFocusLost(); +} diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h new file mode 100644 index 000000000..d8b51d805 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _NUMBEREDIT_H_ +#define _NUMBEREDIT_H_ + +#include "basenumberedit.h" +#include + +class NumberEdit : public BaseNumberEdit { + public: + NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, + std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "NumberEdit(" + std::to_string(getValue()) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + void enable(bool enabled) + { + this->enabled = enabled; + } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setPrefix(std::string value) + { + prefix = std::move(value); + } + + void setSuffix(std::string value) + { + suffix = std::move(value); + } + + void setZeroText(std::string value) + { + zeroText = std::move(value); + } + + void setDisplayHandler(std::function function) + { + displayFunction = std::move(function); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void onFocusLost() override; + + protected: + std::function displayFunction; + std::string prefix; + std::string suffix; + std::string zeroText; + bool enabled = true; + std::function isValueAvailable; +}; + +#endif // _NUMBEREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/page.cpp b/radio/src/thirdparty/libopenui/src/page.cpp new file mode 100644 index 000000000..6f86287ad --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/page.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "page.h" +#include "mainwindow.h" +#include "keyboard_number.h" +#include "keyboard_text.h" +#include "keyboard_curve.h" +#include "opentx.h" // TODO for constants + +PageHeader::PageHeader(Page * parent, uint8_t icon): + Window(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE), + icon(icon) +#if defined(HARDWARE_TOUCH) + , back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + [=]() -> uint8_t { + parent->deleteLater(); + return 0; + }, BUTTON_NOFOCUS) +#endif +{ +} + +void PageHeader::paint(BitmapBuffer * dc) +{ + theme->drawPageHeader(dc, this); +} + +Page::Page(unsigned icon): + Window(&mainWindow, {0, 0, LCD_W, LCD_H}, OPAQUE), + header(this, icon), + body(this, { 0, MENU_HEADER_HEIGHT, LCD_W, LCD_H - MENU_HEADER_HEIGHT }), + previousFocus(focusWindow) +{ + setFocus(); +} + +Page::~Page() +{ +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif +} + +void Page::deleteLater() +{ + if (previousFocus) { + previousFocus->setFocus(); + } + + Window::deleteLater(); +} + +void Page::paint(BitmapBuffer * dc) +{ + dc->clear(TEXT_BGCOLOR); +} + +#if defined(HARDWARE_KEYS) +void Page::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + killEvents(event); + deleteLater(); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Page::onTouchEnd(coord_t x, coord_t y) +{ + if (Window::onTouchEnd(x, y)) + return true; + + TextKeyboard::instance()->disable(true); + NumberKeyboard::instance()->disable(true); + CurveKeyboard::instance()->disable(true); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/page.h b/radio/src/thirdparty/libopenui/src/page.h new file mode 100644 index 000000000..a43d054a3 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/page.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _PAGE_H_ +#define _PAGE_H_ + +#include "window.h" +#include "button.h" + +class Page; + +class PageHeader: public Window { + public: + PageHeader(Page * parent, uint8_t icon); + + void paint(BitmapBuffer * dc) override; + + uint8_t getIcon() const + { + return icon; + } + + protected: + uint8_t icon; +#if defined(HARDWARE_TOUCH) + IconButton back; +#endif +}; + +class Page: public Window { + public: + explicit Page(unsigned icon); + + ~Page() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Page"; + } +#endif + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override + { + Window::onTouchStart(x, y); + return true; + } + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override + { + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; + } +#endif + + void deleteLater(); + + void paint(BitmapBuffer * dc) override; + + protected: + PageHeader header; + FormWindow body; + Window * previousFocus; +}; + +#endif // _PAGE_H_ diff --git a/radio/src/thirdparty/libopenui/src/progress.cpp b/radio/src/thirdparty/libopenui/src/progress.cpp new file mode 100644 index 000000000..a29707719 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/progress.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "progress.h" +#include "theme.h" + +void Progress::paint(BitmapBuffer * dc) +{ + theme->drawProgressBar(dc, 0, 0, width(), height(), value); +} diff --git a/radio/src/thirdparty/libopenui/src/progress.h b/radio/src/thirdparty/libopenui/src/progress.h new file mode 100644 index 000000000..e135b3715 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/progress.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _PROGRESS_H_ +#define _PROGRESS_H_ + +#include "window.h" + +class Progress : public Window { + public: + Progress(Window * parent, const rect_t & rect): + Window(parent, rect) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Progress"; + } +#endif + + void setValue(int value) + { + this->value = value; + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + + protected: + int value; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp new file mode 100644 index 000000000..ff981bdcf --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "slider.h" +#include "theme.h" + +void Slider::paint(BitmapBuffer * dc) +{ + theme->drawSlider(dc, vmin, vmax, getValue(), rect, editMode, hasFocus()); +} + +int Slider::value(coord_t x) const +{ + return vmin + ((vmax - vmin) * x + (rect.w / 2)) / rect.w; +} + +#if defined(HARDWARE_KEYS) +void Slider::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + if (event == EVT_ROTARY_RIGHT) { + setValue(getValue() + rotencSpeed); + return; + } + else if (event == EVT_ROTARY_LEFT) { + setValue(getValue() - rotencSpeed); + return; + } + } + + FormField::onKeyEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Slider::onTouchStart(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + } + sliding = (value(x) == getValue()); + return true; +} + +bool Slider::onTouchEnd(coord_t x, coord_t y) +{ + setValue(value(x)); + invalidate(); + return true; +} + +bool Slider::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + if (hasFocus() && sliding) { + int newValue = value(x); + if (getValue() != newValue) { + setValue(newValue); + invalidate(); + } + } + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h new file mode 100644 index 000000000..3087a8682 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _SLIDER_H_ +#define _SLIDER_H_ + +#include "form.h" + +class Slider : public FormField { + public: + Slider(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + _setValue(std::move(setValue)) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Slider"; + } +#endif + + void setValue(int32_t value) + { + _setValue(limit(vmin, value, vmax)); + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override; + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; +#endif + + protected: + int value(coord_t x) const; + int32_t vmin; + int32_t vmax; + bool sliding = false; + std::function getValue; + std::function _setValue; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp new file mode 100644 index 000000000..3be2d73de --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "static.h" +#include "font.h" + +void StaticText::paint(BitmapBuffer * dc) +{ + if (flags & BUTTON_BACKGROUND) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + if (flags & CENTERED) + dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | flags); + else + dc->drawText(0, 0, text.c_str(), flags); +} diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h new file mode 100644 index 000000000..9bbcde75d --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _STATIC_H_ +#define _STATIC_H_ + +#include +#include "window.h" +#include "button.h" // TODO just for BUTTON_BACKGROUND + +class StaticText : public Window { + public: + StaticText(Window * parent, const rect_t & rect, std::string text = "", LcdFlags flags = 0) : + Window(parent, rect), + text(std::move(text)), + flags(flags) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "StaticText \"" + text + "\""; + } +#endif + + void setFlags(LcdFlags flags) + { + this->flags = flags; + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + + void setText(std::string value) + { + text = std::move(value); + invalidate(); + } + + protected: + std::string text; + LcdFlags flags; +}; + +class Subtitle: public StaticText { + public: + Subtitle(Window * parent, const rect_t & rect, const char * text): + StaticText(parent, rect, text, BOLD) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Subtitle \"" + text + "\""; + } +#endif +}; + +class StaticBitmap: public Window { + public: + StaticBitmap(Window * parent, const rect_t & rect, const char * filename): + Window(parent, rect), + bitmap(BitmapBuffer::load(filename)) + { + } + + StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap): + Window(parent, rect), + bitmap(bitmap) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "StaticBitmap"; + } +#endif + + void paint(BitmapBuffer * dc) override + { + dc->drawBitmap(0, 0, bitmap); + } + + protected: + const BitmapBuffer * bitmap; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp new file mode 100644 index 000000000..2c2b31365 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "tabsgroup.h" +#include "mainwindow.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_number.h" +#include "keyboard_text.h" +#include "keyboard_curve.h" +#endif + +#include "opentx.h" // TODO for constants... +#include "view_main.h" + +TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): + Window(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE), +#if defined(HARDWARE_TOUCH) + back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + [=]() -> uint8_t { + parent->deleteLater(); + return 1; + }, BUTTON_NOFOCUS), +#endif + icon(icon), + carousel(this, parent) +{ +} + +void TabsGroupHeader::paint(BitmapBuffer * dc) +{ + static_cast(theme)->drawMenuBackground(dc, icon, title); +} + +TabsCarousel::TabsCarousel(Window * parent, TabsGroup * menu): + Window(parent, { MENU_HEADER_BUTTONS_LEFT, 0, LCD_W - MENU_HEADER_BUTTONS_LEFT, MENU_HEADER_HEIGHT + 10 }, TRANSPARENT), + menu(menu) +{ +} + +void TabsCarousel::updateInnerWidth() +{ + setInnerWidth(padding_left + MENU_HEADER_BUTTON_WIDTH * menu->tabs.size()); +} + +void TabsCarousel::paint(BitmapBuffer * dc) +{ + static_cast(theme)->drawMenuHeader(dc, menu->tabs, currentIndex); +} + +#if defined(HARDWARE_TOUCH) +bool TabsCarousel::onTouchEnd(coord_t x, coord_t y) +{ + unsigned index = (x - padding_left) / MENU_HEADER_BUTTON_WIDTH; + menu->setCurrentTab(index); + setCurrentIndex(index); + return true; +} +#endif + +TabsGroup::TabsGroup(uint8_t icon): + Window(&mainWindow, { 0, 0, LCD_W, LCD_H }, OPAQUE), + header(this, icon), + body(this, { 0, MENU_BODY_TOP, LCD_W, MENU_BODY_HEIGHT }) +{ +} + +TabsGroup::~TabsGroup() +{ +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif + + for (auto tab: tabs) { + delete tab; + } + + body.deleteChildren(); +} + +void TabsGroup::addTab(PageTab * page) +{ + tabs.push_back(page); + if (!currentTab) { + setCurrentTab(0); + } + header.carousel.updateInnerWidth(); +} + +void TabsGroup::removeTab(unsigned index) +{ + if (currentTab == tabs[index]) { + setCurrentTab(max(0, index - 1)); + } + tabs.erase(tabs.begin() + index); + header.carousel.updateInnerWidth(); +} + +void TabsGroup::removeAllTabs() +{ + for (auto * tab: tabs) { + delete tab; + } + tabs.clear(); + currentTab = nullptr; + header.carousel.updateInnerWidth(); +} + +void TabsGroup::setVisibleTab(PageTab * tab) +{ + if (tab != currentTab) { + setFocus(); + FormField::clearCurrentField(); + body.clear(); +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif + currentTab = tab; + tab->build(&body); + header.setTitle(tab->title.c_str()); + invalidate(); + } +} + +void TabsGroup::checkEvents() +{ + Window::checkEvents(); + if (currentTab) { + currentTab->checkEvents(); + } +} + +#if defined(HARDWARE_KEYS) +void TabsGroup::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_PGDN)) { + uint8_t current = header.carousel.getCurrentIndex() + 1; + setCurrentTab(current >= tabs.size() ? 0 : current); + } + else if (event == EVT_KEY_LONG(KEY_PGDN)) { + killEvents(event); + uint8_t current = header.carousel.getCurrentIndex(); + setCurrentTab(current == 0 ? tabs.size() - 1 : current - 1); + } + else if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + killEvents(event); + ViewMain::instance->setFocus(); + deleteLater(); + } + else if (parent) { + parent->onKeyEvent(event); + } +} +#endif + +void TabsGroup::paint(BitmapBuffer * dc) +{ + dc->clear(TEXT_BGCOLOR); +} + +#if defined(HARDWARE_TOUCH) +bool TabsGroup::onTouchEnd(coord_t x, coord_t y) +{ + if (Window::onTouchEnd(x, y)) + return true; + + TextKeyboard::instance()->disable(true); + NumberKeyboard::instance()->disable(true); + CurveKeyboard::instance()->disable(true); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.h b/radio/src/thirdparty/libopenui/src/tabsgroup.h new file mode 100644 index 000000000..182bab6bc --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/tabsgroup.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _TABSGROUP_H_ +#define _TABSGROUP_H_ + +#include +#include "button.h" +#include "form.h" + +class TabsGroup; + +class PageTab { + friend class TabsCarousel; + friend class TabsGroup; + + public: + PageTab(std::string title, unsigned icon): + title(std::move(title)), + icon(icon) + { + } + + virtual ~PageTab() = default; + + virtual void build(FormWindow * window) = 0; + + virtual void checkEvents() + { + } + + void destroy() + { + if (onPageDestroyed) { + onPageDestroyed(); + } + delete this; + } + + void setOnPageDestroyedHandler(std::function handler) + { + onPageDestroyed = std::move(handler); + } + + void setTitle(std::string value) + { + title = std::move(value); + } + + unsigned getIcon() const + { + return icon; + } + + protected: + std::string title; + unsigned icon; + std::function onPageDestroyed; +}; + +class TabsCarousel: public Window { + public: + TabsCarousel(Window * parent, TabsGroup * menu); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsCarousel"; + } +#endif + + inline void setCurrentIndex(uint8_t index) + { + currentIndex = index; + } + + inline uint8_t getCurrentIndex() + { + return currentIndex; + } + + void updateInnerWidth(); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + constexpr static uint8_t padding_left = 3; + TabsGroup * menu; + uint8_t currentIndex = 0; +}; + +class TabsGroupHeader: public Window { + friend class TabsGroup; + + public: + TabsGroupHeader(TabsGroup * menu, uint8_t icon); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsGroupHeader"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + void setTitle(const char * value) + { + title = value; + } + + protected: +#if defined(HARDWARE_TOUCH) + IconButton back; +#endif + uint8_t icon; + TabsCarousel carousel; + const char * title = nullptr; +}; + +class TabsGroup: public Window { + friend class TabsCarousel; + + public: + explicit TabsGroup(uint8_t icon); + + ~TabsGroup() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsGroup"; + } +#endif + + void addTab(PageTab * page); + + void removeTab(unsigned index); + + void removeAllTabs(); + + void setCurrentTab(unsigned index) + { + if (index < tabs.size()) { + header.carousel.setCurrentIndex(index); + setVisibleTab(tabs[index]); + } + } + + void checkEvents() override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + TabsGroupHeader header; + FormWindow body; + std::vector tabs; + PageTab * currentTab = nullptr; + + void setVisibleTab(PageTab * tab); +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp new file mode 100644 index 000000000..39dbc4ee9 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "textedit.h" +#include "font.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_text.h" +#endif + +void TextEdit::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + if (editMode) { + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, TEXT_INVERTED_COLOR); + coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); + char s[] = { value[cursorPos], '\0' }; + dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP + 1, getTextWidth(s, 1) + 1, height() - 2, TEXT_INVERTED_COLOR); + dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, TEXT_COLOR); + } + else { + const char * displayedValue = value; + LcdFlags textColor; + if (strlen(value) == 0) { + displayedValue = "---"; + textColor = hasFocus() ? TEXT_INVERTED_BGCOLOR : CURVE_AXIS_COLOR; + } + else if (hasFocus()) { + textColor = TEXT_INVERTED_BGCOLOR; + } + else { + textColor = TEXT_COLOR; + } + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); + } + +#if defined(HARDWARE_TOUCH) + auto keyboard = TextKeyboard::instance(); + if (hasFocus() && keyboard->getField() == this) { + coord_t cursorPos = keyboard->getCursorPos(); + dc->drawSolidFilledRect(cursorPos + 2, FIELD_PADDING_TOP, 2, 21, 0); // TEXT_INVERTED_BGCOLOR); + } +#endif +} + +#if defined(HARDWARE_KEYS) +void TextEdit::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + int c = value[cursorPos]; + int v = c; + + switch (event) { + case EVT_ROTARY_RIGHT: + for (int i = 0; i < rotencSpeed; i++) { + v = getNextChar(v); + } + break; + + case EVT_ROTARY_LEFT: + for (int i = 0; i < rotencSpeed; i++) { + v = getPreviousChar(v); + } + break; + + case EVT_KEY_BREAK(KEY_LEFT): + if (cursorPos > 0) { + cursorPos--; + invalidate(); + } + break; + + case EVT_KEY_BREAK(KEY_RIGHT): + if (cursorPos < length - 1) { + cursorPos++; + invalidate(); + } + break; + + case EVT_KEY_BREAK(KEY_ENTER): + if (cursorPos < length - 1) { + cursorPos++; + invalidate(); + } + else { + FormField::onKeyEvent(event); + } + break; + + case EVT_KEY_BREAK(KEY_EXIT): + cursorPos = 0; + FormField::onKeyEvent(event); + break; + + case EVT_KEY_LONG(KEY_ENTER): + if (v == 0) { + killEvents(event); + FormField::onKeyEvent(EVT_KEY_BREAK(KEY_ENTER)); + break; + } + // no break + + case EVT_KEY_LONG(KEY_LEFT): + case EVT_KEY_LONG(KEY_RIGHT): + if (v >= -26 && v <= 26) { + v = -v; // toggle case + if (event == EVT_KEY_LONG(KEY_LEFT)) { + killEvents(KEY_LEFT); + } + } + break; + } + + if (c != v) { + // TRACE("value[%d] = %d", cursorPos, v); + value[cursorPos] = v; + invalidate(); + // TODO storageDirty(...); + } + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool TextEdit::onTouchEnd(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + } + + auto keyboard = TextKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + keyboard->setCursorPos(x); + + return true; +} + +void TextEdit::onFocusLost() +{ + TextKeyboard::instance()->disable(true); + // TODO storageDirty(...); +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h new file mode 100644 index 000000000..b30230cd6 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _TEXTEDIT_H_ +#define _TEXTEDIT_H_ + +#include "form.h" + +class TextEdit : public FormField { + friend class TextKeyboard; + + public: + TextEdit(Window * parent, const rect_t &rect, char * value, uint8_t length, LcdFlags flags = 0) : + FormField(parent, rect), + value(value), + length(length) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextEdit"; + } +#endif + + uint8_t getMaxLength() + { + return length; + } + + char * getData() + { + return value; + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; + + void onFocusLost() override; +#endif + + protected: + char * value; + uint8_t length; + uint8_t cursorPos = 0; + + static uint8_t getPreviousChar(uint8_t c) + { + if (c == 'A') + return ' '; + else if (c == 'a') + return 'Z'; + else if (c == '0') + return 'z'; + else if (c == ' ') + return '>'; + else + return c - 1; + } + + static uint8_t getNextChar(uint8_t c) + { + if (c == ' ') + return 'A'; + else if (c == 'Z') + return 'a'; + else if (c == 'z') + return '0'; + else if (c == '>') + return ' '; + else + return c + 1; + } + +}; + +#endif // _TEXTEDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h new file mode 100644 index 000000000..a57a3be06 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _THEME_H_ +#define _THEME_H_ + +#include "bitmapbuffer.h" + +class PageHeader; + +enum IconState { + STATE_DEFAULT, + STATE_PRESSED, +}; + +class Theme { + public: + virtual void drawMessageBox(const char * title, const char * text, const char * action, LcdFlags flags) const = 0; + virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const = 0; + virtual void drawCheckBox(BitmapBuffer * dc, bool value, bool focus) const = 0; + virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; + virtual void drawPageHeader(BitmapBuffer * dc, const PageHeader * page) const = 0; + virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0; + virtual const BitmapBuffer * getIconMask(uint8_t index) const = 0; +}; + +extern Theme * theme; + +#endif // _THEME_H_ diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp new file mode 100644 index 000000000..ed0805ee8 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "window.h" + +Window * Window::focusWindow = nullptr; +std::list Window::trash; + +Window::Window(Window * parent, const rect_t & rect, uint8_t flags): + parent(parent), + rect(rect), + innerWidth(rect.w), + innerHeight(rect.h), + windowFlags(flags) +{ + if (parent) { + parent->addChild(this); + if (!(flags & TRANSPARENT)) { + invalidate(); + } + } +} + +Window::~Window() +{ + TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); + + if (focusWindow == this) { + focusWindow = nullptr; + } +} + +void Window::attach(Window * window) +{ + if (parent) + detach(); + parent = window; + parent->addChild(this); +} + +void Window::detach() +{ + if (parent) { + parent->removeChild(this); + parent = nullptr; + } +} + +void Window::deleteLater(bool detach) +{ + TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); + + if (static_cast(focusWindow) == static_cast(this)) { + focusWindow = nullptr; + } + + if (detach) + this->detach(); + else + parent = nullptr; + + if (closeHandler) { + closeHandler(); + } + + trash.push_back(this); +} + +void Window::clear() +{ + scrollPositionX = 0; + scrollPositionY = 0; + innerWidth = rect.w; + innerHeight = rect.h; + deleteChildren(); + invalidate(); +} + +void Window::deleteChildren() +{ + for (auto window: children) { + window->deleteLater(false); + } + children.clear(); +} + +void Window::clearFocus() +{ + if (focusWindow) { + focusWindow->onFocusLost(); + focusWindow = nullptr; + } +} + +void Window::setFocus() +{ + TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); + + if (focusWindow != this) { + clearFocus(); + focusWindow = this; + if (focusHandler) { + focusHandler(); + } + } + + Window * parent = this->parent; + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + + if (parent) { + parent->scrollTo(this); + invalidate(); + } +} + +void Window::setScrollPositionX(coord_t value) +{ + coord_t newScrollPosition = max(0, min(innerWidth - width(), value)); + if (newScrollPosition != scrollPositionX) { + scrollPositionX = newScrollPosition; + invalidate(); + } +} + +void Window::setScrollPositionY(coord_t value) +{ + coord_t newScrollPosition = max(0, min(innerHeight - height(), value)); + if (newScrollPosition != scrollPositionY) { + scrollPositionY = newScrollPosition; + invalidate(); + } +} + +void Window::scrollTo(Window * child) +{ + coord_t offsetY = 0; + Window * parent = child->getParent(); + while (parent && parent != this) { + offsetY += parent->top(); + parent = parent->getParent(); + } + + coord_t top = offsetY + child->top(); + coord_t bottom = offsetY + child->bottom(); + + if (top < scrollPositionY) { + setScrollPositionY(top - 5); + } + else if (bottom > scrollPositionY + height() - 5) { + setScrollPositionY(bottom - height() + 5); + } +} + +void Window::fullPaint(BitmapBuffer * dc) +{ + TRACE_WINDOWS("%s", getWindowDebugString().c_str()); + + if (windowFlags & PAINT_CHILDREN_FIRST) { + coord_t xmin, xmax, ymin, ymax; + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + dc->getClippingRect(xmin, xmax, ymin, ymax); + paintChildren(dc); + dc->setOffset(x, y); + dc->setClippingRect(xmin, xmax, ymin, ymax); + } + + paint(dc); + + if (!(windowFlags & NO_SCROLLBAR)) { + drawVerticalScrollbar(dc); + } + + if (!(windowFlags & PAINT_CHILDREN_FIRST)) { + paintChildren(dc); + } +} + +bool Window::isChildFullSize(Window * child) +{ + return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); +} + +bool Window::isChildVisible(Window * window) +{ + for (auto rit = children.rbegin(); rit != children.rend(); rit++) { + auto child = *rit; + if (child == window) { + return true; + } + if ((child->getWindowFlags() & OPAQUE) & isChildFullSize(child)) { + return false; + } + } + return false; +} + +void Window::paintChildren(BitmapBuffer * dc) +{ + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + + auto it = children.end(); + + while(it != children.begin()) { + auto child = *(--it); + if ((child->windowFlags & OPAQUE) && isChildFullSize(child)) { + break; + } + } + + for (; it != children.end(); it++) { + auto child = *it; + + coord_t child_xmin = x + child->rect.x; + if (child_xmin >= xmax) + continue; + coord_t child_ymin = y + child->rect.y; + if (child_ymin >= ymax) + continue; + coord_t child_xmax = child_xmin + child->rect.w; + if (child_xmax <= xmin) + continue; + coord_t child_ymax = child_ymin + child->rect.h; + if (child_ymax <= ymin) + continue; + + dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); + dc->setClippingRect(max(xmin, x + child->rect.left()), + min(xmax, x + child->rect.right()), + max(ymin, y + child->rect.top()), + min(ymax, y + child->rect.bottom())); + child->fullPaint(dc); + } +} + +void Window::checkEvents() +{ + auto copy = children; + for (auto child: copy) { + child->checkEvents(); + } + +#if defined(HARDWARE_KEYS) + if (this == focusWindow) { + event_t event = getEvent(false); + if (event) { + TRACE_WINDOWS("Event 0x%x received ...", event); + this->onKeyEvent(event); + } + } +#endif + + if (windowFlags & REFRESH_ALWAYS) { + invalidate(); + } +} + +#if defined(HARDWARE_KEYS) +void Window::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString().c_str(), event); + if (parent) { + parent->onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Window::onTouchStart(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(x, y, child->rect)) { + if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchEnd(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(x, y, child->rect)) { + if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(startX, startY, child->rect)) { + if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { + return true; + } + } + } + + if (slideY && innerHeight > rect.h) { + setScrollPositionY(scrollPositionY - slideY); + return true; + } + + if (slideX && innerWidth > rect.w) { + setScrollPositionX(scrollPositionX - slideX); + return true; + } + + return false; +} +#endif + +void Window::adjustInnerHeight() +{ + innerHeight = 0; + for (auto child: children) { + innerHeight = max(innerHeight, child->rect.y + child->rect.h); + } +} + +coord_t Window::adjustHeight() +{ + adjustInnerHeight(); + coord_t old = rect.h; + rect.h = innerHeight; + return rect.h - old; +} + +void Window::moveWindowsTop(coord_t y, coord_t delta) +{ + for (auto child: children) { + if (child->rect.y > y) { + child->rect.y += delta; + } + } + innerHeight += delta; +} + +void Window::invalidate(const rect_t & rect) +{ + if (isVisible()) { + parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); + } +} + +void Window::drawVerticalScrollbar(BitmapBuffer * dc) +{ + if (innerHeight > rect.h) { + coord_t x = rect.w - 3; + coord_t y = scrollPositionY + 3; + coord_t h = rect.h - 6; + // lcd->drawSolidFilledRect(x, y, 1, h, LINE_COLOR); + coord_t yofs = (h*scrollPositionY + innerHeight/2) / innerHeight; + coord_t yhgt = (h*rect.h + innerHeight/2) / innerHeight; + if (yhgt + yofs > h) + yhgt = h - yofs; + if (yhgt < 15) { + yhgt = 15; + if (y + yofs + yhgt > innerHeight) { + yofs = innerHeight - y - yhgt; + } + } + dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, SCROLLBOX_COLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h new file mode 100644 index 000000000..d99644216 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _WINDOW_H_ +#define _WINDOW_H_ + +#include +#include +#include +#include +#include +#include +#include "bitmapbuffer.h" +#include "libopenui_defines.h" +#include "libopenui_helpers.h" +#include "libopenui_config.h" + +// OPAQUE/TRANSPARENT defined in WinGDI +#ifndef OPAQUE + #define OPAQUE 1 + #define TRANSPARENT 2 +#endif + +constexpr uint8_t NO_SCROLLBAR = 4; +constexpr uint8_t FORWARD_SCROLL = 8; +constexpr uint8_t REFRESH_ALWAYS = 16; +constexpr uint8_t BORDER_FOCUS_ONLY = 32; +constexpr uint8_t PAINT_CHILDREN_FIRST = 64; + +class Window { + friend class GridLayout; + + public: + Window(Window * parent, const rect_t & rect, uint8_t flags=0); + + virtual ~Window(); + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() + { + return "Window"; + } + + std::string getRectString() + { + char result[32]; + sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); + return result; + } + + std::string getIndentString() + { + std::string result; + auto tmp = parent; + while (tmp) { + result += " "; + tmp = tmp->getParent(); + } + return result; + } + + std::string getWindowDebugString() + { + return getIndentString() + getName() + " " + getRectString(); + } +#endif + + Window * getParent() const + { + return parent; + } + + uint8_t getWindowFlags() const + { + return windowFlags; + } + + void setWindowFlags(uint8_t flags) + { + windowFlags = flags; + } + + void setCloseHandler(std::function handler) + { + closeHandler = std::move(handler); + } + + void setFocusHandler(std::function handler) + { + focusHandler = std::move(handler); + } + + void deleteLater(bool detach=true); + + void clear(); + + void deleteChildren(); + + bool hasFocus() const + { + return focusWindow == this; + } + + static Window * getFocus() + { + return focusWindow; + } + + void scrollTo(Window * child); + + static void clearFocus(); + + void setFocus(); + + void setRect(rect_t rect) + { + this->rect = rect; + invalidate(); + } + + void setWidth(coord_t value) + { + rect.w = value; + invalidate(); + } + + void setHeight(coord_t value) + { + rect.h = value; + if (innerHeight <= value) { + setScrollPositionY(0); + } + invalidate(); + } + + coord_t getHeight() + { + return rect.h; + } + + void setLeft(coord_t x) + { + rect.x = x; + invalidate(); + } + + void setTop(coord_t y) + { + rect.y = y; + invalidate(); + } + + coord_t left() const + { + return rect.x; + } + + coord_t right() const + { + return rect.x + rect.w; + } + + coord_t top() const + { + return rect.y; + } + + coord_t bottom() const + { + return rect.y + rect.h; + } + + coord_t width() const + { + return rect.w; + } + + coord_t height() const + { + return rect.h; + } + + rect_t getRect() const + { + return rect; + } + + void setInnerWidth(coord_t w) + { + innerWidth = w; + if (width() >= w) { + scrollPositionX = 0; + } + } + + void setInnerHeight(coord_t h) + { + innerHeight = h; + if (height() >= h) { + scrollPositionY = 0; + } + } + + coord_t getScrollPositionY() const + { + return scrollPositionY; + } + + void setScrollPositionX(coord_t value); + + void setScrollPositionY(coord_t value); + + bool isChildVisible(Window * window); + + bool isChildFullSize(Window * window); + + bool isVisible() + { + return parent && parent->isChildVisible(this); + } + + virtual void paint(BitmapBuffer * dc) + { + } + + void drawVerticalScrollbar(BitmapBuffer * dc); + + void paintChildren(BitmapBuffer * dc); + + void fullPaint(BitmapBuffer * dc); + + bool pointInRect(coord_t x, coord_t y, rect_t & rect) + { + return (x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h); + } + + virtual void onFocusLost() + { + TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); + invalidate(); + }; + +#if defined(HARDWARE_KEYS) + virtual void onKeyEvent(event_t event); +#endif + +#if defined(HARDWARE_TOUCH) + virtual bool onTouchStart(coord_t x, coord_t y); + + virtual bool onTouchEnd(coord_t x, coord_t y); + + virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); +#endif + + void adjustInnerHeight(); + + coord_t adjustHeight(); + + void moveWindowsTop(coord_t y, coord_t delta); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void bringToTop() + { + attach(parent); // does a detach + attach + } + + virtual void checkEvents(); + + protected: + Window * parent; + std::list children; + rect_t rect; + coord_t innerWidth; + coord_t innerHeight; + coord_t scrollPositionX = 0; + coord_t scrollPositionY = 0; + uint8_t windowFlags; + + static Window * focusWindow; + static std::list trash; + + std::function closeHandler; + std::function focusHandler; + + void attach(Window * window); + + void detach(); + + void addChild(Window * window) + { + children.push_back(window); + } + + void removeChild(Window * window) + { + children.remove(window); + invalidate(); + } + + virtual void invalidate(const rect_t & rect); +}; + +#endif // _WINDOW_H_