chore: pull libopenui into the EdgeTX repository

This commit is contained in:
gagarinlg
2023-05-18 14:49:35 +02:00
92 changed files with 14119 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
---
Language: Cpp
BasedOnStyle: Google
BreakBeforeBraces: Linux
BreakConstructorInitializers: AfterColon
...
@@ -0,0 +1 @@
src/thirdparty/stb/
@@ -0,0 +1,7 @@
[submodule "thirdparty/stb"]
path = thirdparty/stb
url = https://github.com/nothings/stb.git
[submodule "thirdparty/lvgl"]
path = thirdparty/lvgl
url = https://github.com/EdgeTX/lvgl.git
branch = release/v8.2
+32
View File
@@ -0,0 +1,32 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="160" />
<Objective-C>
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
<option name="INDENT_VISIBILITY_KEYWORDS" value="2" />
<option name="FUNCTION_BRACE_PLACEMENT" value="2" />
<option name="SUPERCLASS_LIST_BEFORE_COLON" value="0" />
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
<option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
</Objective-C>
<codeStyleSettings language="CMake">
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="8" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="ObjectiveC">
<option name="CLASS_BRACE_STYLE" value="2" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="8" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>
+5
View File
@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
+165
View File
@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+4
View File
@@ -0,0 +1,4 @@
# libopenui
C++ UI Library primarily used in EdgeTX for Colour LCD targets.
Was forked from [OpenTX/libopenui](https://github.com/opentx/libopenui) and detached so that contributors can easily work on both if they wish to do so.
+57
View File
@@ -0,0 +1,57 @@
if(HARDWARE_KEYS)
add_definitions(-DHARDWARE_KEYS)
endif()
if(HARWARE_TOUCH)
add_definitions(-DHARDWARE_TOUCH)
endif()
set(LIBOPENUI_SRC
libopenui_file.cpp
bitmapbuffer.cpp
window.cpp
layer.cpp
form.cpp
button.cpp
button_matrix.cpp
static.cpp
checkbox.cpp
carousel.cpp
slider.cpp
mainwindow.cpp
menu.cpp
menutoolbar.cpp
choice.cpp
choiceex.cpp
filechoice.cpp
numberedit.cpp
textedit.cpp
progress.cpp
table.cpp
modal_window.cpp
dialog.cpp
keyboard_text.cpp
keyboard_number.cpp
keyboard_base.cpp
# LVGL widgets
widgets/window_base.c
widgets/simple_btn.c
widgets/field_edit.c
widgets/edgetx_table.c
)
foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC})
set(LIBOPENUI_SRC_FILES ${LIBOPENUI_SRC_FILES} ${LIBOPENUI_SRC_DIR}/src/${LIBOPENUI_FILE})
endforeach()
set(SRC ${SRC} ${LIBOPENUI_SRC_FILES}
${LIBOPENUI_SRC_DIR}/thirdparty/lz4/lz4.c
)
# add_library(libopenui_native OBJECT ${LIBOPENUI_SRC_FILES})
# add_library(libopenui_stm32 OBJECT ${LIBOPENUI_SRC_FILES})
# Dirty hack to include FatFs into libopenui
# TODO: this must go away
#target_include_directories(libopenui PRIVATE
# ${CMAKE_CURRENT_SOURCE_DIR}/../../FatFs)
+223
View File
@@ -0,0 +1,223 @@
/*
* 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.
*/
#pragma once
#include "form.h"
#include "strhelpers.h"
class BaseNumberEdit: public FormField
{
public:
BaseNumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax,
std::function<int()> getValue,
std::function<void(int)> setValue = nullptr,
WindowFlags windowFlags = 0, LcdFlags textFlags = 0,
LvglCreate objConstruct = nullptr) :
FormField(parent, rect, windowFlags, textFlags, objConstruct),
vmin(vmin),
vmax(vmax),
_getValue(std::move(getValue)),
_setValue(std::move(setValue))
{
}
void setMin(int value)
{
vmin = value;
}
void setMax(int value)
{
vmax = value;
}
void setDefault(int value)
{
vdefault = value;
}
int32_t getMin() const
{
return vmin;
}
int32_t getMax() const
{
return vmax;
}
int32_t getDefault() const
{
return vdefault;
}
void setStep(int value)
{
step = value;
}
int32_t getStep() const
{
return step;
}
void setFastStep(int value)
{
fastStep = value;
}
int32_t getFastStep() const
{
return fastStep;
}
void setAccelFactor(int value)
{
accelFactor = value;
}
int32_t getAccelFactor() const
{
return accelFactor;
}
void setValue(int value)
{
auto newValue = limit(vmin, value, vmax);
if (newValue != currentValue) {
currentValue = newValue;
if (instantChange && _setValue != nullptr) {
_setValue(currentValue);
}
}
if (lvobj != nullptr) {
std::string str;
if (displayFunction != nullptr) {
str = displayFunction(currentValue);
} else {
str = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(),
suffix.c_str());
}
lv_textarea_set_text(lvobj, str.c_str());
}
}
void setPrefix(std::string value)
{
prefix = std::move(value);
update();
}
void setSuffix(std::string value)
{
suffix = std::move(value);
update();
}
void setZeroText(std::string value)
{
zeroText = std::move(value);
update();
}
void enableInstantChange(bool value)
{
instantChange = value;
}
void disableInstantChange()
{
enableInstantChange(false);
}
void setSetValueHandler(std::function<void(int)> handler)
{
_setValue = std::move(handler);
}
void setGetValueHandler(std::function<int()> handler)
{
_getValue = std::move(handler);
}
int32_t getValue() const
{
return (editMode && !instantChange) ? currentValue
: _getValue != nullptr ? _getValue()
: 0;
}
void setEditMode(bool newEditMode) override
{
auto previousEditMode = editMode;
FormField::setEditMode(newEditMode);
if (!instantChange) {
if (!previousEditMode && newEditMode) {
if (_getValue != nullptr) currentValue = _getValue();
}
else if (previousEditMode && !newEditMode) {
if (_setValue != nullptr) _setValue(currentValue);
}
}
}
void setDisplayHandler(std::function<std::string(int value)> function)
{
displayFunction = std::move(function);
update();
}
virtual void update()
{
if (_getValue == nullptr) return;
auto newValue = _getValue();
if (newValue != currentValue) {
currentValue = newValue;
}
if (lvobj != nullptr) {
std::string str;
if (displayFunction != nullptr) {
str = displayFunction(currentValue);
} else {
str = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(),
suffix.c_str());
}
lv_textarea_set_text(lvobj, str.c_str());
}
}
virtual void paint(BitmapBuffer* dc) override {};
protected:
int vdefault = 0;
int vmin;
int vmax;
int step = 1;
int fastStep = 10;
int accelFactor = 4;
int currentValue;
bool instantChange = true;
std::string prefix;
std::string suffix;
std::string zeroText;
std::function<int()> _getValue;
std::function<void(int)> _setValue;
std::function<std::string(int)> displayFunction;
};
+80
View File
@@ -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.
*/
#pragma once
#include <inttypes.h>
// A set of bitfield handling macros
template <typename T>
inline T bfBit(uint8_t n)
{
return T(1) << n;
}
#define BFBIT_FLIP(y, mask) ( y ^= (mask) )
//! Create a bitmask of length 'len'.
template <typename T>
inline T bfBitmask(uint8_t len)
{
return bfBit<T>(len) - 1;
}
//! Create a bitfield mask of length 'len' starting at bit 'start'.
template <typename T>
inline T bfMask(uint8_t start, uint8_t len)
{
return bfBitmask<T>(len) << start;
}
//! Prepare a bitmask for insertion or combining.
template <typename T>
inline T bfPrep(T x, uint8_t start, uint8_t len)
{
return (x & bfBitmask<T>(len)) << start;
}
//! Extract a bitfield of length 'len' starting at bit 'start' from 'y'.
template <typename T>
inline T bfGet(T y, uint8_t start, uint8_t len)
{
return ((y)>>(start)) & bfBitmask<T>(len);
}
//! Insert 'len' bits of 'x 'into 'y', starting at bit 'start' from 'y'.
template <class T>
inline T bfSet(T to, T from, uint8_t start, uint8_t len)
{
return (to & ~bfMask<T>(start, len)) | bfPrep<T>(from, start, len);
}
template <class T>
inline T bfBitGet(T y, T mask)
{
return (y & mask);
}
template <class T>
inline T bfSingleBitGet(T y, uint8_t i)
{
return bfBitGet(y, bfBit<T>(i));
}
File diff suppressed because it is too large Load Diff
+475
View File
@@ -0,0 +1,475 @@
/*
* 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.
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "libopenui_types.h"
#include "libopenui_defines.h"
#include "libopenui_depends.h"
#include "libopenui_helpers.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 += this->offsetX; y += this->offsetY
#define RESTORE_OFFSET() this->offsetX = offsetX, this->offsetY = offsetY
#if defined(LCD_VERTICAL_INVERT) && 0
#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_INVALID = -1,
BMP_8BIT = 0,
BMP_RGB565,
BMP_ARGB4444
};
struct _lv_draw_ctx_t;
typedef _lv_draw_ctx_t lv_draw_ctx_t;
struct _lv_obj_t;
typedef _lv_obj_t lv_obj_t;
template<class T>
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;
}
inline void clearOffset()
{
setOffset(0, 0);
}
inline void reset()
{
clearOffset();
clearClippingRect();
}
coord_t getOffsetX() const
{
return offsetX;
}
coord_t getOffsetY() const
{
return offsetY;
}
inline uint8_t getFormat() const
{
return format;
}
inline uint16_t width() const
{
return _width;
}
inline uint16_t height() const
{
return _height;
}
inline T * getData() const
{
return data;
}
uint32_t getDataSize() const
{
return _width * _height * sizeof(T);
}
inline const pixel_t * getPixelPtrAbs(coord_t x, coord_t y) const
{
#if defined(LCD_VERTICAL_INVERT) && 0
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<const uint16_t> Bitmap;
class RLEBitmap:
public BitmapBufferBase<uint16_t>
{
public:
RLEBitmap(uint8_t format, const uint8_t* rle_data);
~RLEBitmap();
static int decode(uint8_t * dest, unsigned int destSize, const uint8_t * src);
};
class BitmapBuffer: public BitmapBufferBase<pixel_t>
{
private:
bool dataAllocated;
#if defined(DEBUG)
bool leakReported;
#endif
lv_obj_t* canvas = nullptr;
lv_draw_ctx_t* draw_ctx = nullptr;
public:
BitmapBuffer(uint8_t format, uint16_t width, uint16_t height);
BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, uint16_t * data);
~BitmapBuffer();
void setDrawCtx(lv_draw_ctx_t* ctx) { draw_ctx = ctx; }
void setData(uint16_t* d) {
if (!dataAllocated) {
data = d;
data_end = d + (_width * _height);
}
}
inline void setFormat(uint8_t format)
{
this->format = format;
}
inline void clear(LcdFlags flags=0)
{
drawSolidFilledRect(0, 0, _width - offsetX, _height - offsetY, flags);
}
inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const
{
APPLY_OFFSET();
coord_t w = 1, h = 1;
if (!applyClippingRect(x, y, w, h))
return nullptr;
return getPixelPtrAbs(x, y);
}
inline void drawPixel(coord_t x, coord_t y, pixel_t value)
{
APPLY_OFFSET();
coord_t w = 1, h = 1;
if (!applyClippingRect(x, y, w, h))
return;
drawPixelAbs(x, y, value);
}
void drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color);
inline void drawAlphaPixel(coord_t x, coord_t y, uint8_t opacity, pixel_t value)
{
APPLY_OFFSET();
coord_t w = 1, h = 1;
if (!applyClippingRect(x, y, w, h))
return;
drawAlphaPixelAbs(x, y, opacity, value);
}
void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = 0);
void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = 0);
void drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att);
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, uint8_t opacity = 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, uint8_t opacity = 0);
void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0);
void drawFilledTriangle(coord_t x1, coord_t y1, coord_t x2, coord_t y2, coord_t x3, coord_t y3, LcdFlags flags = 0, uint8_t opacity = 0);
void drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0);
void drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0);
void drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags = 0);
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 * loadBitmap(const char * filename, BitmapFormats fmt = BMP_INVALID);
static BitmapBuffer * loadRamBitmap(const uint8_t * buffer, int len);
static BitmapBuffer * loadMask(const char * filename);
static BitmapBuffer * load8bitMask(const uint8_t * lbm);
static BitmapBuffer * load8bitMaskLZ4(const uint8_t * compressed_data);
static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background);
static BitmapBuffer * load8bitMaskOnBackground(const uint8_t * lbm, LcdFlags foreground, LcdFlags background);
static uint8_t * loadFont(const uint8_t * lbm, int len, int& w, int& h);
uint8_t * to8bitMask(size_t* size) const;
void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offsetX = 0, coord_t width = 0);
void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, const BitmapBuffer * srcBitmap, coord_t offsetX = 0, coord_t offsetY = 0, coord_t width = 0, coord_t height = 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);
coord_t drawText(coord_t x, coord_t y, const char * s, LcdFlags flags = 0)
{
if (!s) return x;
return drawSizedText(x, y, s, strlen(s), flags);
}
coord_t drawTextAtIndex(coord_t x, coord_t y, const char *const *s, uint8_t idx, LcdFlags flags = 0)
{
return drawText(x, y, s[idx], flags);
}
static void formatNumberAsString(char *buffer, const uint8_t buffer_size, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr);
coord_t 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<class T>
void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, float scale = 0);
template<class T>
void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h);
BitmapBuffer * horizontalFlip() const;
BitmapBuffer * verticalFlip() const;
BitmapBuffer * invertMask() const;
protected:
static BitmapBuffer * load_bmp(const char * filename);
static BitmapBuffer * load_stb(const char * filename, BitmapFormats fmt = BMP_INVALID);
static BitmapBuffer * load_stb_buffer(const uint8_t * buffer, int len);
static BitmapBuffer * convert_stb_bitmap(uint8_t * img, int w, int h, int n,
BitmapFormats fmt = BMP_INVALID);
inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) const
{
if (h < 0) {
y += h;
h = -h;
}
if (w < 0) {
x += w;
w = -w;
}
if (x >= xmax || y >= ymax)
return false;
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;
return data && h > 0 && w > 0;
}
template <class T>
void drawBitmapAbs(coord_t x, coord_t y, const T* bmp, coord_t srcx = 0,
coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0,
float scale = 0);
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 * getPixelPtrAbs(coord_t x, coord_t y) const
{
#if defined(LCD_VERTICAL_INVERT) && 0
x = _width - x - 1;
y = _height - y - 1;
#endif
return &data[y * _width + x];
}
inline pixel_t * getPixelPtrAbs(coord_t x, coord_t y)
{
#if defined(LCD_VERTICAL_INVERT) && 0
x = _width - x - 1;
y = _height - y - 1;
#endif
return &data[y * _width + x];
}
inline void drawPixelAbs(coord_t x, coord_t y, pixel_t value)
{
pixel_t * p = getPixelPtrAbs(x, y);
drawPixel(p, value);
}
inline void drawAlphaPixelAbs(coord_t x, coord_t y, uint8_t opacity, uint16_t color)
{
pixel_t * p = getPixelPtrAbs(x, y);
drawAlphaPixel(p, opacity, color);
}
void drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags, uint8_t opacity);
bool liangBarskyClipper(coord_t& x1, coord_t& y1, coord_t& x2, coord_t& y2);
};
class LZ4Bitmap : public BitmapBuffer
{
public:
LZ4Bitmap(uint8_t format, const uint8_t* compressed_data);
~LZ4Bitmap();
};
class LZ4Mask : public BitmapBufferBase<uint8_t>
{
public:
LZ4Mask(const uint8_t* compressed_data);
~LZ4Mask();
};
// Back buffer to draw
extern BitmapBuffer * lcd;
// Buffer currently on display
extern BitmapBuffer * lcdFront;
+124
View File
@@ -0,0 +1,124 @@
/*
* 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.
*/
#pragma once
#include "window.h"
template <class T>
class BufferedWindow: public T
{
public:
using T::T;
~BufferedWindow() override
{
delete bitmap;
}
void invalidate(const rect_t & rect) override
{
paintUpdateNeeded = true;
T::invalidate(rect);
}
protected:
BitmapBuffer * bitmap = nullptr;
bool paintUpdateNeeded = false;
virtual void paintUpdate(BitmapBuffer * dc) = 0;
};
template <class T>
class OpaqueBufferedWindow: public BufferedWindow<T>
{
public:
using BufferedWindow<T>::BufferedWindow;
void paint(BitmapBuffer * dc) override
{
if (!this->bitmap) {
this->bitmap = new BitmapBuffer(BMP_RGB565, T::width(), T::height());
this->paintUpdateNeeded = true;
}
if (this->paintUpdateNeeded) {
this->paintUpdate(this->bitmap);
this->paintUpdateNeeded = false;
}
dc->drawBitmap(0, 0, this->bitmap);
}
};
template <class T>
class TransparentBufferedWindow: public BufferedWindow<T>
{
public:
using BufferedWindow<T>::BufferedWindow;
void paint(BitmapBuffer * dc) override
{
if (!BufferedWindow<T>::bitmap) {
this->bitmap = new BitmapBuffer(BMP_RGB565, T::width(), T::height());
this->paintUpdateNeeded = true;
}
if (this->paintUpdateNeeded) {
this->paintUpdate(dc);
// we can only store the bitmap if fully drawn
coord_t xmin, xmax, ymin, ymax;
dc->getClippingRect(xmin, xmax, ymin, ymax);
if (xmax - xmin >= this->width() && ymax - ymin >= this->height()) {
this->bitmap->drawBitmap(0, 0, dc, dc->getOffsetX(), dc->getOffsetY());
this->paintUpdateNeeded = false;
}
}
else {
dc->drawBitmap(0, 0, this->bitmap);
}
}
};
class TransparentBitmapBackground: public TransparentBufferedWindow<Window>
{
public:
TransparentBitmapBackground(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap):
TransparentBufferedWindow<Window>(parent, rect, OPAQUE),
background(bitmap)
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "TransparentBitmapBackground";
}
#endif
protected:
const BitmapBuffer * background;
void paintUpdate(BitmapBuffer * dc) override
{
if (background) {
dc->drawBitmap((width() - background->width()) / 2, (height() - background->height()) / 2, background);
}
}
};
+119
View File
@@ -0,0 +1,119 @@
/*
* 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"
#include "widgets/simple_btn.h"
static void update_checked_flag(lv_obj_t* obj, WindowFlags flags)
{
if (!(flags & BUTTON_CHECKED))
lv_obj_clear_state(obj, LV_STATE_CHECKED);
else
lv_obj_add_state(obj, LV_STATE_CHECKED);
}
Button::Button(Window* parent, const rect_t& rect,
std::function<uint8_t(void)> pressHandler,
WindowFlags windowFlag, LcdFlags textFlags,
LvglCreate objConstruct) :
FormField(parent, rect, windowFlag, textFlags,
objConstruct ? objConstruct : simple_btn_create),
pressHandler(std::move(pressHandler))
{
lv_obj_add_event_cb(lvobj, Button::long_pressed, LV_EVENT_LONG_PRESSED, nullptr);
update_checked_flag(lvobj, windowFlags);
}
void Button::check(bool checked)
{
if (_deleted) return;
if (checked != bool(windowFlags & BUTTON_CHECKED)) {
windowFlags ^= BUTTON_CHECKED;
update_checked_flag(lvobj, windowFlags);
invalidate();
}
}
bool Button::checked() const
{
if (windowFlags & BUTTON_CHECKED_ON_FOCUS)
return hasFocus();
else
return windowFlags & BUTTON_CHECKED;
}
void Button::onPress()
{
check(pressHandler && pressHandler());
}
void Button::onLongPress()
{
if (longPressHandler) {
check(longPressHandler());
lv_obj_clear_state(lvobj, LV_STATE_PRESSED);
lv_indev_wait_release(lv_indev_get_act());
}
}
void Button::onClicked()
{
onPress();
}
#if defined(HARDWARE_TOUCH)
// this needs to stay so that viewmain doesnt get the touch event
bool Button::onTouchEnd(coord_t x, coord_t y) { return true; }
#endif
void Button::checkEvents()
{
Window::checkEvents();
if (checkHandler) checkHandler();
}
void Button::long_pressed(lv_event_t* e)
{
auto obj = lv_event_get_target(e);
auto btn = (Button*)lv_obj_get_user_data(obj);
if (obj) btn->onLongPress();
}
TextButton::TextButton(Window* parent, const rect_t& rect, std::string text,
std::function<uint8_t(void)> pressHandler,
WindowFlags windowFlags) :
Button(parent, rect, std::move(pressHandler), windowFlags, 0, lv_btn_create),
text(std::move(text))
{
update_checked_flag(lvobj, windowFlags);
label = lv_label_create(lvobj);
lv_label_set_text(label, this->text.c_str());
lv_obj_center(label);
}
void IconButton::paint(BitmapBuffer * dc)
{
dc->drawBitmap(0, 0, theme->getIcon(icon, checked() ? STATE_PRESSED : STATE_DEFAULT));
}
+131
View File
@@ -0,0 +1,131 @@
/*
* 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.
*/
#pragma once
#include "form.h"
constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1u;
constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2u;
constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u;
class Button : public FormField
{
public:
Button(Window* parent, const rect_t& rect,
std::function<uint8_t(void)> pressHandler = nullptr,
WindowFlags windowFlags = 0, LcdFlags textFlags = 0,
LvglCreate objConstruct = nullptr);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "Button"; }
#endif
virtual void onPress();
virtual void onLongPress();
void onClicked() override;
void checkEvents() override;
void check(bool checked = true);
bool checked() const;
void setPressHandler(std::function<uint8_t(void)> handler)
{
pressHandler = std::move(handler);
}
void setLongPressHandler(std::function<uint8_t(void)> handler)
{
longPressHandler = std::move(handler);
}
void setCheckHandler(std::function<void(void)> handler)
{
checkHandler = std::move(handler);
}
#if defined(HARDWARE_TOUCH)
bool onTouchEnd(coord_t x, coord_t y) override;
#endif
protected:
std::function<uint8_t(void)> pressHandler;
std::function<uint8_t(void)> longPressHandler;
std::function<void(void)> checkHandler;
static void long_pressed(lv_event_t* e);
};
class TextButton: public Button
{
public:
TextButton(Window* parent, const rect_t& rect, std::string text,
std::function<uint8_t(void)> pressHandler = nullptr,
WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "TextButton \"" + text + "\"";
}
#endif
void setText(std::string value)
{
if (value != text) {
text = std::move(value);
lv_label_set_text(label, text.c_str());
}
}
void setBgColorHandler(std::function<LcdFlags(void)> handler = nullptr)
{
bgColorHandler = std::move(handler);
}
protected:
lv_obj_t * label = nullptr;
std::string text;
std::function<LcdFlags(void)> bgColorHandler = nullptr;
};
class IconButton: public Button
{
public:
IconButton(Window* parent, const rect_t& rect, uint8_t icon,
std::function<uint8_t(void)> pressHandler,
WindowFlags flags = 0) :
Button(parent, rect, std::move(pressHandler), flags), icon(icon)
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "IconButton(" + std::to_string(icon) + ")";
}
#endif
void paint(BitmapBuffer * dc) override;
protected:
uint8_t icon;
};
+148
View File
@@ -0,0 +1,148 @@
/*
* Copyright (C) EdgeTX
*
* Source:
* https://github.com/EdgeTX/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 "button_matrix.h"
#include <stdlib.h>
#include <string.h>
static const char _filler[] = "0";
static const char _newline[] = "\n";
static const char _map_end[] = "";
static void btn_matrix_event(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t* obj = lv_event_get_target(e);
auto btn_id = (uint32_t *)lv_event_get_param(e);
auto btnm = (ButtonMatrix*)lv_event_get_user_data(e);
bool edited = lv_obj_has_state(obj, LV_STATE_EDITED);
bool is_pointer = lv_indev_get_type(lv_indev_get_act()) == LV_INDEV_TYPE_POINTER;
if (edited || is_pointer)
btnm->onPress((uint8_t)*btn_id);
}
else if (code == LV_EVENT_DRAW_PART_BEGIN) {
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
if (dsc->part == LV_PART_ITEMS) {
auto btnm = (ButtonMatrix*)lv_event_get_user_data(e);
if (btnm->isActive((uint8_t)dsc->id)) {
dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_ACTIVE);
dsc->label_dsc->color = makeLvColor(COLOR_THEME_PRIMARY2);
} else {
dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_PRIMARY2);
dsc->label_dsc->color = makeLvColor(COLOR_THEME_SECONDARY1);
}
} else if (dsc->part == LV_PART_MAIN) {
lv_obj_t* obj = lv_event_get_target(e);
if (lv_obj_has_state(obj, LV_STATE_FOCUS_KEY) &&
!lv_obj_has_state(obj, LV_STATE_EDITED)) {
dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_FOCUS);
dsc->rect_dsc->bg_opa = LV_OPA_20;
}
}
}
}
ButtonMatrix::ButtonMatrix(Window* parent, const rect_t& r) :
FormField(parent, r, 0, 0, lv_btnmatrix_create)
{
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_event_cb(lvobj, btn_matrix_event, LV_EVENT_ALL, this);
}
ButtonMatrix::~ButtonMatrix()
{
deallocate();
}
void ButtonMatrix::deallocate()
{
if (txt_cnt == 0) return;
for (uint8_t i = 0; i < txt_cnt; i++) {
char* txt = lv_btnm_map[i];
if (txt != _filler && txt != _newline && txt != _map_end) free(txt);
}
free(lv_btnm_map);
free(txt_index);
txt_cnt = 0;
btn_cnt = 0;
}
void ButtonMatrix::initBtnMap(uint8_t cols, uint8_t btns)
{
deallocate();
uint8_t rows = ((btns - 1) / cols) + 1;
txt_cnt = btns + rows;
btn_cnt = btns;
lv_btnm_map = (char**)malloc(sizeof(char*) * txt_cnt);
txt_index = (uint8_t*)malloc(sizeof(uint8_t) * btns);
uint8_t col = 0;
uint8_t btn = 0;
uint8_t txt_i = 0;
while (btn < btns) {
if (col == cols) {
lv_btnm_map[txt_i++] = (char*)_newline;
col = 0;
}
txt_index[btn] = txt_i;
lv_btnm_map[txt_i++] = (char*)_filler;
btn++;
col++;
}
lv_btnm_map[txt_i++] = (char*)_map_end;
}
void ButtonMatrix::setText(uint8_t btn_id, const char* txt)
{
if (btn_id >= btn_cnt) return;
uint8_t txt_i = txt_index[btn_id];
lv_btnm_map[txt_i] = strdup(txt);
}
void ButtonMatrix::update()
{
lv_btnmatrix_set_map(lvobj, (const char**)lv_btnm_map);
lv_btnmatrix_ctrl_t ctrl = LV_BTNMATRIX_CTRL_CLICK_TRIG |
LV_BTNMATRIX_CTRL_NO_REPEAT;
lv_btnmatrix_set_btn_ctrl_all(lvobj, ctrl);
}
void ButtonMatrix::onClicked()
{
lv_group_focus_obj(lvobj);
FormField::onClicked();
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (C) EdgeTX
*
* Source:
* https://github.com/EdgeTX/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.
*/
#pragma once
#include "form.h"
class ButtonMatrix : public FormField
{
char** lv_btnm_map = nullptr;
uint8_t* txt_index = nullptr;
uint8_t txt_cnt = 0;
uint8_t btn_cnt = 0;
public:
ButtonMatrix(Window* parent, const rect_t& rect);
~ButtonMatrix();
virtual void onPress(uint8_t btn_id) {}
virtual bool isActive(uint8_t btn_id) { return false; }
protected:
void deallocate();
void initBtnMap(uint8_t cols, uint8_t btns);
void setText(uint8_t btn_id, const char* txt);
void update();
void onClicked() override;
};
+68
View File
@@ -0,0 +1,68 @@
/*
* 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 "carousel.h"
void CarouselWindow::update()
{
auto first = min<int>(selection - 2, items.size() - count);
first = max(0, first);
coord_t lastPosition = 0;
coord_t spacing = 10;
if (!items.empty() && count > 0)
spacing = (width() - items[0]->back->width() * (count - 1) - items[0]->front->width()) / (count - 1);
int index = 0;
for (auto & item: items) {
Window * window;
if (index == selection) {
item->back->detach();
window = item->front;
}
else {
item->front->detach();
window = item->back;
}
window->attach(this);
window->setLeft(lastPosition);
window->setTop((height() - window->height()) / 2);
lastPosition += window->width() + spacing;
index += 1;
}
}
#if defined(HARDWARE_KEYS)
void Carousel::onEvent(event_t event)
{
TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event);
if (event == EVT_ROTARY_RIGHT) {
if (body->selection < (int)body->items.size() - 1)
select(body->selection + 1);
}
else if (event == EVT_ROTARY_LEFT) {
if (body->selection > 0)
select(body->selection - 1);
}
else {
Window::onEvent(event);
}
}
#endif
+151
View File
@@ -0,0 +1,151 @@
/*
* 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.
*/
#pragma once
#include <vector>
#include "button.h"
class CarouselItem
{
public:
CarouselItem(Window * front, Window * back):
front(front),
back(back)
{
}
void setSelectHandler(std::function<void()> handler)
{
selectHandler = std::move(handler);
}
virtual ~CarouselItem() = default;
public:
Window * front;
Window * back;
std::function<void()> selectHandler;
};
class CarouselWindow: public Window
{
friend class Carousel;
public:
CarouselWindow(Window * parent, const rect_t & rect, uint8_t count):
Window(parent, rect, NO_SCROLLBAR),
count(count)
{
}
void deleteLater(bool detach = true, bool trash = true) override // NOLINT(google-default-arguments)
{
if (_deleted)
return;
clear();
Window::deleteLater(detach, trash);
}
void addItem(CarouselItem * item)
{
items.push_back(item);
update();
}
void clear()
{
// children will be deleted later (front and back)
children.clear();
for (auto & item: items) {
item->front->deleteLater();
item->back->deleteLater();
delete item;
}
items.clear();
}
void select(int index, bool scroll = true)
{
selection = index;
if (selection >= 0) {
auto item = items[selection];
if (item->selectHandler) {
item->selectHandler();
}
}
update();
}
protected:
std::vector<CarouselItem *> items;
int selection = 0;
unsigned count;
void update();
};
class Carousel: public Window
{
public:
Carousel(Window * parent, const rect_t & rect):
Window(parent, rect, NO_SCROLLBAR)
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Carousel";
}
#endif
void addItem(CarouselItem * item)
{
body->addItem(item);
}
void clear()
{
body->clear();
}
void select(int index)
{
body->select(index);
previousButton->enable(index > 0);
nextButton->enable(index < (int)body->items.size() - 1);
}
int getSelection() const
{
return body->selection;
}
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override;
#endif
protected:
Button * previousButton = nullptr;
Button * nextButton = nullptr;
CarouselWindow * body = nullptr;
};
+64
View File
@@ -0,0 +1,64 @@
/*
* 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"
static void checkbox_event_handler(lv_event_t* e)
{
lv_obj_t* target = lv_event_get_target(e);
CheckBox* cb = (CheckBox*)lv_obj_get_user_data(target);
if (cb) cb->setValue(lv_obj_get_state(target) & LV_STATE_CHECKED);
}
CheckBox::CheckBox(Window* parent, const rect_t& rect,
std::function<uint8_t()> getValue,
std::function<void(uint8_t)> setValue, WindowFlags flags) :
FormField(parent, rect, flags, 0, lv_switch_create),
_getValue(std::move(getValue)),
_setValue(std::move(setValue))
{
// TODO: migrate to default theme
if (height() > 0)
lv_obj_set_width(lvobj, (lv_coord_t)(height() * 1.7f));
// PART_MAIN
lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_PART_MAIN);
lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_STATE_FOCUSED);
update();
lv_obj_add_event_cb(lvobj, checkbox_event_handler,
LV_EVENT_VALUE_CHANGED, this);
}
void CheckBox::update() const
{
if (!_getValue) return;
if (_getValue()) lv_obj_add_state(lvobj, LV_STATE_CHECKED);
else lv_obj_clear_state(lvobj, LV_STATE_CHECKED);
}
void CheckBox::onClicked()
{
// prevent FormField::onClicked()
}
+60
View File
@@ -0,0 +1,60 @@
/*
* 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.
*/
#pragma once
#include <utility>
#include "form.h"
class CheckBox : public FormField
{
public:
CheckBox(Window* parent, const rect_t& rect,
std::function<uint8_t()> getValue,
std::function<void(uint8_t)> setValue, WindowFlags flags = 0);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "CheckBox"; }
#endif
void onClicked() override;
const char* getLabel() const { return label.c_str(); }
void setLabel(std::string newLabel) { label = std::move(newLabel); }
uint8_t getValue() const { return _getValue(); }
void setValue(uint8_t value) { _setValue(value); }
void setSetValueHandler(std::function<void(uint8_t)> handler)
{
_setValue = std::move(handler);
}
void setGetValueHandler(std::function<uint8_t()> handler)
{
_getValue = std::move(handler);
}
void update() const;
protected:
std::string label;
std::function<uint8_t()> _getValue;
std::function<void(uint8_t)> _setValue;
};
+245
View File
@@ -0,0 +1,245 @@
/*
* 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"
#include "theme.h"
void choice_changed_cb(lv_event_t *e)
{
auto code = lv_event_get_code(e);
if (code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t *target = lv_event_get_target(e);
if (target != nullptr) {
ChoiceBase *cb = (ChoiceBase*)lv_obj_get_user_data(target);
if (cb) {
std::string text = cb->getLabelText();
lv_label_set_text(cb->label, text.c_str());
}
}
}
}
ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type,
WindowFlags windowFlags) :
FormField(parent, rect, windowFlags), type(type)
{
lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
lv_obj_set_width(lvobj, LV_SIZE_CONTENT);
lv_obj_set_layout(lvobj, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW);
if (height() == 0)
{
lv_obj_set_style_pad_top(lvobj, 4, LV_PART_MAIN);
lv_obj_set_style_pad_bottom(lvobj, 4, LV_PART_MAIN);
lv_obj_set_style_radius(lvobj, 4, LV_PART_MAIN);
}
lv_obj_add_event_cb(lvobj, choice_changed_cb, LV_EVENT_VALUE_CHANGED, lvobj);
label = lv_label_create(lvobj);
lv_group_t * def_group = lv_group_get_default();
if(def_group) {
lv_group_add_obj(def_group, lvobj);
}
// Normal font and background color
lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN);
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
lv_obj_set_style_pad_right(lvobj, 5, LV_PART_MAIN);
lv_obj_set_style_border_width(lvobj, 1, LV_PART_MAIN);
lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN);
lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN);
// focused
lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_FOCUSED);
lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED);
lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN);
lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN);
lv_obj_set_style_pad_right(label, CHOICE_LABEL_MARGIN_RIGHT, LV_PART_MAIN);
// lv_obj_set
// add the image
lv_obj_t *img = lv_img_create(lvobj);
lv_img_set_src(img, type == CHOICE_TYPE_DROPOWN ? LV_SYMBOL_DOWN : LV_SYMBOL_DIRECTORY);
lv_obj_set_align(img, LV_ALIGN_RIGHT_MID);
}
std::string Choice::getLabelText()
{
std::string text;
if (textHandler != nullptr) {
if (_getValue) { text = textHandler(_getValue()); }
} else if (_getValue) {
int val = _getValue();
val -= vmin;
if (val >= 0 && val < (int)values.size()) {
text = values[val];
}
}
return text;
}
Choice::Choice(Window* parent, const rect_t& rect, int vmin, int vmax,
std::function<int()> _getValue,
std::function<void(int)> _setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
vmin(vmin),
vmax(vmax),
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
Choice::Choice(Window* parent, const rect_t& rect, const char* const values[],
int vmin, int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
vmin(vmin),
vmax(vmax),
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
setValues(values);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
Choice::Choice(Window* parent, const rect_t& rect,
std::vector<std::string> values, int vmin, int vmax,
std::function<int()> _getValue, std::function<void(int)> _setValue,
WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
values(std::move(values)),
vmin(vmin),
vmax(vmax),
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin,
int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue, WindowFlags windowFlags) :
ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags),
vmin(vmin),
vmax(vmax),
_getValue(std::move(_getValue)),
_setValue(std::move(_setValue))
{
if (values) {
uint8_t len = values[0];
const char* value = &values[1];
for (int i = vmin; i <= vmax; i++) {
this->values.emplace_back(
std::string(value, min<uint8_t>(len, strlen(value))));
value += len;
}
}
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
void Choice::addValue(const char * value)
{
values.emplace_back(value);
vmax += 1;
}
void Choice::addValues(const char * const values[], uint8_t count)
{
this->values.reserve(this->values.size() + count);
for (uint8_t i = 0; i < count; i++)
this->values.emplace_back(values[i]);
vmax += count;
}
void Choice::setValues(std::vector<std::string> values)
{
this->values = std::move(values);
}
void Choice::setValues(const char * const values[])
{
this->values.clear();
if (values) {
auto value = &values[0];
for (int i = vmin; i <= vmax; i++) {
this->values.emplace_back(*value++);
}
}
}
void Choice::setValue(int val)
{
if (_setValue) {
_setValue(val);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
}
void Choice::onClicked()
{
openMenu();
}
void Choice::fillMenu(Menu *menu, const FilterFct& filter)
{
menu->removeLines();
auto value = _getValue();
int count = 0;
int selectedIx = -1;
for (int i = vmin; i <= vmax; ++i) {
if (filter && !filter(i)) continue;
if (isValueAvailable && !isValueAvailable(i)) continue;
if (textHandler) {
menu->addLineBuffered(textHandler(i), [=]() { setValue(i); });
} else if (unsigned(i - vmin) < values.size()) {
menu->addLineBuffered(values[i - vmin], [=]() { setValue(i); });
} else {
menu->addLineBuffered(std::to_string(i), [=]() { setValue(i); });
}
if (value == i) { selectedIx = count; }
++count;
}
menu->updateLines();
if (selectedIx >= 0) { menu->select(selectedIx); }
}
void Choice::openMenu()
{
setEditMode(true); // this needs to be done first before menu is created.
auto menu = new Menu(this);
if (!menuTitle.empty()) menu->setTitle(menuTitle);
fillMenu(menu);
if (beforeDisplayMenuHandler) {
beforeDisplayMenuHandler(menu);
}
menu->setCloseHandler([=]() { setEditMode(false); });
}
+189
View File
@@ -0,0 +1,189 @@
/*
* 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.
*/
#pragma once
#include <vector>
#include "form.h"
class Menu;
constexpr int CHOICE_LABEL_MARGIN_RIGHT = 10;
enum ChoiceType {
CHOICE_TYPE_DROPOWN,
CHOICE_TYPE_FOLDER,
};
class ChoiceBase : public FormField
{
public:
ChoiceBase(Window *parent, const rect_t &rect,
ChoiceType type = CHOICE_TYPE_DROPOWN,
WindowFlags windowFlags = 0);
inline ChoiceType getType() const { return type; }
protected:
ChoiceType type;
lv_obj_t* label;
friend void choice_changed_cb(lv_event_t *e);
virtual std::string getLabelText() { return ""; };
};
class Choice: public ChoiceBase {
public:
Choice(Window* parent, const rect_t &rect, int vmin, int vmax,
std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
Choice(Window* parent, const rect_t &rect,
std::vector<std::string> values, int vmin, int vmax,
std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
Choice(Window* parent, const rect_t &rect, const char *const values[],
int vmin, int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
Choice(Window* parent, const rect_t &rect, const char *values, int vmin,
int vmax, std::function<int()> _getValue,
std::function<void(int)> _setValue = nullptr,
WindowFlags windowFlags = 0);
void addValue(const char *value);
void addValues(const char *const values[], uint8_t count);
void setValues(std::vector<std::string> values);
void setValues(const char *const values[]);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Choice";
}
#endif
void onClicked() override;
void setBeforeDisplayMenuHandler(std::function<void(Menu*)> handler)
{
beforeDisplayMenuHandler = std::move(handler);
}
void setSetValueHandler(std::function<void(int)> handler)
{
_setValue = std::move(handler);
}
void setGetValueHandler(std::function<int()> handler)
{
_getValue = std::move(handler);
}
void setAvailableHandler(std::function<bool(int)> handler)
{
isValueAvailable = std::move(handler);
}
unsigned getIndexFromValue(int value) const
{
if (!isValueAvailable) {
return value - vmin;
}
unsigned index = 0;
for (int i = vmin; i < value; i++) {
if (isValueAvailable(i)) {
index++;
}
}
return index;
}
int getValueFromIndex(int index) const
{
if (!isValueAvailable) {
return vmin + index;
}
int value = vmin - 1;
while (index >= 0) {
index--;
value++;
while (value < vmax && !isValueAvailable(value)) {
value++;
}
}
return value;
}
void setValue(int val);
int getIntValue() const { return _getValue(); }
unsigned getValuesCount() const
{
return getIndexFromValue(vmax + 1);
}
void setTextHandler(std::function<std::string(int)> handler)
{
textHandler = std::move(handler);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
void setMenuTitle(std::string value)
{
menuTitle = std::move(value);
}
void setMin(int value)
{
vmin = value;
invalidate();
}
void setMax(int value)
{
vmax = value;
invalidate();
}
protected:
friend class MenuToolbar;
std::string getLabelText() override;
std::vector<std::string> values;
int vmin = 0;
int vmax = 0;
std::string menuTitle;
std::function<int()> _getValue;
std::function<void(int)> _setValue;
std::function<bool(int)> isValueAvailable;
std::function<std::string(int)> textHandler;
std::function <void(Menu *)> beforeDisplayMenuHandler;
typedef std::function<bool(int16_t)> FilterFct;
void fillMenu(Menu *menu, const FilterFct& filter = nullptr);
virtual void openMenu();
};
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*/
#include "choiceex.h"
static void localLongPressHandler(lv_event_t* e)
{
lv_eventData_t* ld = (lv_eventData_t*)lv_event_get_user_data(e);
ld->isLongPressed = true;
ld->lv_LongPressHandler(ld->userData);
}
void ChoiceEx::set_lv_LongPressHandler(lvHandler_t longPressHandler, void* data)
{
TRACE("longPressHandler=%p", longPressHandler);
if (longPressHandler) {
longPressData.userData = data;
longPressData.lv_LongPressHandler = longPressHandler;
lv_obj_add_event_cb(lvobj, localLongPressHandler, LV_EVENT_LONG_PRESSED,
&longPressData);
lv_obj_add_event_cb(lvobj, ClickHandler, LV_EVENT_CLICKED, this);
}
}
ChoiceEx::ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin,
int16_t vmax, std::function<int16_t()> getValue,
std::function<void(int16_t)> setValue,
WindowFlags windowFlags) :
Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags)
{
longPressData.isLongPressed = false;
}
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*/
#ifndef _CHOICEEX_H_
#define _CHOICEEX_H_
#include "choice.h"
typedef void (*lvHandler_t)(void*);
typedef struct {
void* userData;
bool isLongPressed;
lvHandler_t lv_LongPressHandler;
} lv_eventData_t;
class ChoiceEx : public Choice
{
public:
ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, int16_t vmax,
std::function<int16_t()> getValue,
std::function<void(int16_t)> setValue = nullptr,
WindowFlags windowFlags = 0);
void set_lv_LongPressHandler(lvHandler_t longPressHandler, void* data);
lv_eventData_t longPressData;
void onClicked() override
{
if (!longPressData.isLongPressed) Choice::onClicked();
}
protected:
std::function<void(event_t)> longPressHandler = nullptr;
static void ClickHandler(lv_event_t* e)
{
ChoiceEx* ch = (ChoiceEx*)lv_event_get_user_data(e);
if (ch) ch->longPressData.isLongPressed = false;
}
};
#endif
+84
View File
@@ -0,0 +1,84 @@
/*
* 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 "theme.h"
DialogWindowContent::DialogWindowContent(Dialog* parent, const rect_t& rect) :
ModalWindowContent(parent, rect),
form(this, rect_t{})
{
form.setFlexLayout();
auto form_obj = form.getLvObj();
lv_obj_set_style_pad_all(form_obj, lv_dpx(8), 0);
lv_coord_t max_height = LCD_H * 0.8;
lv_obj_set_style_max_height(form_obj, max_height, 0);
}
void DialogWindowContent::setTitle(const std::string& text)
{
ModalWindowContent::setTitle(text);
if (title) {
lv_coord_t title_h = lv_obj_get_height(title);
lv_obj_set_y(form.getLvObj(), title_h);
}
}
void DialogWindowContent::deleteLater(bool detach, bool trash)
{
if (_deleted) return;
form.deleteLater(true, false);
ModalWindowContent::deleteLater(detach, trash);
}
void DialogWindowContent::updateSize()
{
lv_obj_center(lvobj);
ModalWindowContent::updateSize();
}
#if defined(DEBUG_WINDOWS)
std::string DialogWindowContent::getName() const
{
return "DialogWindowContent";
}
#endif
Dialog::Dialog(Window* parent, std::string title, const rect_t& rect) :
ModalWindow(parent), content(new DialogWindowContent(this, rect))
{
lv_obj_set_style_bg_color(content->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY3), 0);
lv_obj_set_style_bg_opa(content->getLvObj(), LV_OPA_100, LV_PART_MAIN);
bringToTop();
if (!title.empty()) content->setTitle(std::move(title));
}
void Dialog::onCancel()
{
deleteLater();
}
void Dialog::onEvent(event_t event)
{
// block key events
(void)event;
}
+57
View File
@@ -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.
*/
#pragma once
#include "modal_window.h"
#include "form.h"
class Dialog;
class DialogWindowContent : public ModalWindowContent
{
friend class Dialog;
public:
DialogWindowContent(Dialog* parent, const rect_t& rect);
void setTitle(const std::string& text) override;
void deleteLater(bool detach = true, bool trash = true) override;
void updateSize() override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override;
#endif
public:
FormGroup form;
};
class Dialog : public ModalWindow
{
public:
Dialog(Window* parent, std::string title, const rect_t& rect);
protected:
DialogWindowContent* content;
void onCancel() override;
void onEvent(event_t event) override;
};
+139
View File
@@ -0,0 +1,139 @@
/*
* 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"
#include "theme.h"
#include "message_dialog.h"
#include <algorithm>
#if !defined(STR_SDCARD)
#define STR_SDCARD "SD"
#endif
#if !defined(STR_NO_FILES_ON_SD)
#define STR_NO_FILES_ON_SD "No files on SD Card!"
#endif
FileChoice::FileChoice(Window* parent, const rect_t &rect,
std::string folder, const char *extension, int maxlen,
std::function<std::string()> getValue,
std::function<void(std::string)> setValue,
bool stripExtension) :
ChoiceBase(parent, rect, CHOICE_TYPE_FOLDER),
folder(std::move(folder)),
extension(extension),
maxlen(maxlen),
getValue(std::move(getValue)),
setValue(std::move(setValue)),
stripExtension(stripExtension)
{
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
}
std::string FileChoice::getLabelText()
{
return getValue();
}
bool FileChoice::openMenu()
{
FILINFO fno;
DIR dir;
std::list<std::string> 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);
if (extension && (!fnExt || !isExtensionMatching(fnExt, extension)))
continue; // wrong extension
if (stripExtension) fnLen -= extLen;
if (!fnLen || fnLen > maxlen) continue; // wrong size
// eject duplicates
std::string newFile(fno.fname, fnLen);
if (std::find(files.begin(), files.end(), newFile) != files.end())
continue;
files.emplace_back(newFile);
}
if (!files.empty()) {
// sort files
files.sort(compare_nocase);
files.push_front("");
auto menu = new Menu(this);
int count = 0;
int current = -1;
std::string value = getValue();
for (const auto &file : files) {
menu->addLineBuffered(file, [=]() {
setValue(file);
lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr);
});
// 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;
}
menu->updateLines();
if (current >= 0) {
menu->select(current);
}
menu->setCloseHandler([=]() {
editMode = false;
// setFocus(SET_FOCUS_DEFAULT);
});
return true;
}
}
new MessageDialog(this, STR_SDCARD, STR_NO_FILES_ON_SD);
return false;
}
void FileChoice::onClicked()
{
openMenu();
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.
*/
#pragma once
#include "choice.h"
#include <string>
class FileChoice : public ChoiceBase
{
public:
FileChoice(Window* parent, const rect_t& rect, std::string folder,
const char* extension, int maxlen,
std::function<std::string()> getValue,
std::function<void(std::string)> setValue,
bool stripExtension=false);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "FileChoice"; }
#endif
void onClicked() override;
protected:
std::string getLabelText() override;
std::string folder;
const char* extension;
int maxlen;
std::function<std::string()> getValue;
std::function<void(std::string)> setValue;
bool stripExtension;
bool openMenu();
};
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*/
#pragma once
#include <lvgl/lvgl.h>
class FlexGridLayout
{
const lv_coord_t* col_dsc = nullptr;
const lv_coord_t* row_dsc = nullptr;
lv_coord_t padding = 0;
uint8_t col_pos = 0;
uint8_t col_span = 1;
uint8_t row_pos = 0;
uint8_t row_span = 1;
public:
FlexGridLayout(const lv_coord_t col_dsc[], const lv_coord_t row_dsc[],
lv_coord_t padding = 4) :
col_dsc(col_dsc), row_dsc(row_dsc), padding(padding)
{
}
FlexGridLayout(const FlexGridLayout& g) :
col_dsc(g.col_dsc), row_dsc(g.row_dsc), padding(g.padding)
{
}
void apply(Window* w) { apply(w->getLvObj()); }
void apply(lv_obj_t* obj) {
// padding
lv_obj_set_style_pad_all(obj, padding, LV_PART_MAIN);
lv_obj_set_style_pad_row(obj, padding, LV_PART_MAIN);
lv_obj_set_style_pad_column(obj, padding, LV_PART_MAIN);
// layout
lv_obj_set_layout(obj, LV_LAYOUT_GRID);
if (col_dsc && row_dsc) {
lv_obj_set_grid_dsc_array(obj, col_dsc, row_dsc);
}
}
void add(Window* w) { add(w->getLvObj()); }
void add(lv_obj_t* obj) {
if (col_dsc && row_dsc) {
lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_START, col_pos, col_span,
LV_GRID_ALIGN_CENTER, row_pos, row_span);
}
}
void resetPos() { col_pos = 0; row_pos = 0; }
void nextColumn() { col_pos += col_span; }
void nextRow() { row_pos += row_span; col_pos = 0; }
void nextCell()
{
nextColumn();
if (col_dsc[col_pos] == LV_GRID_TEMPLATE_LAST) {
nextRow();
}
}
void setColSpan(uint8_t span) { col_span = span; }
void setRowSpan(uint8_t span) { row_span = span; }
};
+29
View File
@@ -0,0 +1,29 @@
/*
* 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.
*/
#pragma once
#include <lvgl/lvgl.h>
#include "libopenui_types.h"
const lv_font_t* getFont(LcdFlags flags);
uint8_t getFontHeight(LcdFlags flags);
uint8_t getFontHeightCondensed(LcdFlags flags);
int getTextWidth(const char * s, int len = 0, LcdFlags flags = 0);
+150
View File
@@ -0,0 +1,150 @@
/*
* 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"
#include "widgets/window_base.h"
FormField::FormField(Window* parent, const rect_t& rect,
WindowFlags windowFlags, LcdFlags textFlags,
LvglCreate objConstruct) :
Window(parent, rect, windowFlags, textFlags, objConstruct)
{
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
}
void FormField::setEditMode(bool newEditMode)
{
editMode = newEditMode;
if (lvobj != nullptr) {
lv_group_t* grp = (lv_group_t*)lv_obj_get_group(lvobj);
if (grp != nullptr) lv_group_set_editing(grp, editMode);
}
if (editMode && lvobj != nullptr) {
lv_obj_add_state(lvobj, LV_STATE_EDITED);
} else if (lvobj != nullptr) {
lv_obj_clear_state(lvobj, LV_STATE_EDITED);
}
invalidate();
}
void FormField::enable(bool value)
{
if (value) {
lv_obj_clear_state(lvobj, LV_STATE_DISABLED);
} else {
lv_obj_add_state(lvobj, LV_STATE_DISABLED);
}
}
bool FormField::isEnabled() const
{
return !lv_obj_has_state(lvobj, LV_STATE_DISABLED);
}
void FormField::onClicked()
{
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
if(indev_type != LV_INDEV_TYPE_POINTER) {
setEditMode(!editMode);
invalidate();
}
}
void FormField::onCancel()
{
if (isEditMode()) {
setEditMode(false);
invalidate();
} else {
Window::onCancel();
}
}
FormGroup::Line::Line(Window* parent, lv_obj_t* obj, FlexGridLayout* layout) :
Window(parent, obj), layout(layout)
{
construct();
}
FormGroup::Line::Line(Window* parent, FlexGridLayout* layout) :
Window(parent, rect_t{}), layout(layout)
{
construct();
}
void FormGroup::Line::construct()
{
// forward scroll and focus
windowFlags |= FORWARD_SCROLL | NO_FOCUS | FORM_FORWARD_FOCUS;
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
if (layout) {
layout->apply(lvobj);
}
lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
lv_obj_set_width(lvobj, lv_pct(100));
}
void FormGroup::Line::setLayout(FlexGridLayout *l)
{
layout = l;
if (layout) layout->apply(lvobj);
}
void FormGroup::Line::addChild(Window* window)
{
Window::addChild(window);
if (layout) {
layout->add(window->getLvObj());
layout->nextCell();
}
}
FormGroup::FormGroup(Window* parent, const rect_t& rect,
WindowFlags windowflags, LvglCreate objConstruct) :
Window(parent, rect, windowflags, 0, objConstruct)
{
lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN);
}
void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding)
{
lv_obj_set_flex_flow(lvobj, flow);
if (_LV_FLEX_COLUMN & flow) {
lv_obj_set_style_pad_row(lvobj, padding, LV_PART_MAIN);
} else {
lv_obj_set_style_pad_column(lvobj, padding, LV_PART_MAIN);
}
lv_obj_set_width(lvobj, lv_pct(100));
lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
}
FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout, lv_coord_t left_padding)
{
if (layout) layout->resetPos();
auto lv_line = window_create(lvobj);
if (left_padding) lv_obj_set_style_pad_left(lv_line, left_padding, 0);
return new Line(this, lv_line, layout);
}
+119
View File
@@ -0,0 +1,119 @@
/*
* 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.
*/
#pragma once
#include "window.h"
#include "flexlayout.h"
constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 1;
constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 2;
constexpr WindowFlags FORM_BORDER_FOCUS_ONLY = WINDOW_FLAGS_LAST << 3;
constexpr WindowFlags FORM_NO_BORDER = WINDOW_FLAGS_LAST << 4;
constexpr WindowFlags FORM_FLAGS_LAST = FORM_NO_BORDER;
class FormField : public Window
{
public:
FormField(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0,
LcdFlags textFlags = 0, LvglCreate objConstruct = nullptr);
virtual void changeEnd(bool forceChanged = false)
{
if (changeHandler) { changeHandler(); }
}
void setChangeHandler(std::function<void()> handler)
{
changeHandler = std::move(handler);
}
void setBackgroundHandler(std::function<uint32_t(FormField *field)> handler)
{
backgroundHandler = std::move(handler);
}
inline bool isEditMode() const { return editMode; }
virtual void setEditMode(bool newEditMode);
bool isEnabled() const;
void enable(bool value = true);
void disable() { enable(false); }
void onFocusLost() override
{
setEditMode(false);
Window::onFocusLost();
}
void onClicked() override;
void onCancel() override;
protected:
bool editMode = false;
bool enabled = true;
std::function<void()> changeHandler = nullptr;
std::function<uint32_t(FormField *)> backgroundHandler = nullptr;
};
class FormGroup : public Window
{
public:
class Line : public Window
{
FlexGridLayout *layout;
void construct();
public:
Line(Window *parent, lv_obj_t *obj, FlexGridLayout *layout = nullptr);
Line(Window *parent, FlexGridLayout *layout = nullptr);
void setLayout(FlexGridLayout *l);
protected:
void addChild(Window *window) override;
};
FormGroup(Window *parent, const rect_t &rect, WindowFlags windowflags = 0,
LvglCreate objConstruct = nullptr);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "FormGroup"; }
#endif
void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, lv_coord_t padding = 0);
Line* newLine(FlexGridLayout* layout = nullptr, lv_coord_t left_padding = 0);
};
class FormWindow : public FormGroup
{
public:
FormWindow(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0,
LvglCreate objConstruct = nullptr) :
FormGroup(parent, rect, windowFlags, objConstruct)
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "FormWindow"; }
#endif
protected:
void paint(BitmapBuffer *) override {}
};
+122
View File
@@ -0,0 +1,122 @@
/*
* 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.
*/
#pragma once
#include "window.h"
class GridLayout {
public:
explicit GridLayout(coord_t width, coord_t top = 0):
width(width),
currentY(top)
{
}
explicit 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:
explicit FormGridLayout(coord_t width = LCD_W, coord_t top = 0):
GridLayout(width, top)
{
}
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 = PAGE_PADDING;
};
+176
View File
@@ -0,0 +1,176 @@
/*
* Copyright (C) EdgeTX
*
* Source:
* https://github.com/edgetx/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_base.h"
#include "form.h"
#include "mainwindow.h"
Keyboard * Keyboard::activeKeyboard = nullptr;
static void keyboard_event_cb(lv_event_t * e)
{
auto code = lv_event_get_code(e);
if (code == LV_EVENT_READY) {
Keyboard::hide();
} else if (code == LV_EVENT_CANCEL) {
Keyboard::hide();
} else if (code == LV_EVENT_KEY) {
int32_t c = *((int32_t *)lv_event_get_param(e));
if (c == LV_KEY_ESC) {
Keyboard::hide();
}
}
}
static void field_focus_leave(lv_event_t* e)
{
Keyboard::hide();
}
static void _assign_lv_group(lv_group_t* g)
{
// associate it with all input devices
lv_indev_t* indev = lv_indev_get_next(NULL);
while (indev) {
lv_indev_set_group(indev, g);
indev = lv_indev_get_next(indev);
}
}
Keyboard::Keyboard(coord_t height) :
Window(MainWindow::instance(), {0, LCD_H - height, LCD_W, height}, OPAQUE)
{
// set the background of the window and opacity to 100%
lv_obj_set_parent(lvobj, lv_layer_top()); // the keyboard is always on top
lv_obj_set_style_bg_color(lvobj, lv_color_make(0xE0, 0xE0, 0xE0), LV_PART_MAIN);
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
// use a separate group for the keyboard
group = lv_group_create();
lv_group_set_editing(group, true);
auto old_g = lv_group_get_default();
lv_group_set_default(group);
keyboard = lv_keyboard_create(lvobj);
lv_group_set_default(old_g);
lv_obj_add_event_cb(keyboard, keyboard_event_cb, LV_EVENT_ALL, this);
lv_obj_set_pos(keyboard, 0, 0);
lv_obj_set_size(keyboard, LCD_W, height);
// TODO: really needed ???
lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN);
}
Keyboard::~Keyboard()
{
if (group) lv_group_del(group);
}
void Keyboard::clearField()
{
TRACE("CLEAR FIELD");
if (keyboard != nullptr) {
lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN);
}
detach();
lv_obj_set_parent(lvobj, lv_layer_top());
if (fieldContainer) {
lv_obj_scroll_to_y(fieldContainer->getLvObj(), scroll_pos, LV_ANIM_OFF);
fieldContainer = nullptr;
}
if (field) {
// restore field's group
auto obj = field->getLvObj();
if (obj) {
lv_obj_remove_event_cb(obj, field_focus_leave);
}
field->setEditMode(false);
field->changeEnd();
field = nullptr;
if (fieldGroup) {
_assign_lv_group(fieldGroup);
lv_group_set_editing(fieldGroup, false);
fieldGroup = nullptr;
}
}
}
void Keyboard::hide()
{
if (activeKeyboard) {
activeKeyboard->clearField();
lv_obj_add_flag(activeKeyboard->lvobj, LV_OBJ_FLAG_HIDDEN);
activeKeyboard = nullptr;
}
}
bool Keyboard::attachKeyboard()
{
if (activeKeyboard) {
if (activeKeyboard == this) return false;
hide();
}
activeKeyboard = this;
return true;
}
void Keyboard::setField(FormField* newField)
{
if (!attachKeyboard())
return;
lv_obj_t* obj = newField->getLvObj();
if (obj) {
fieldContainer = newField->getFullScreenWindow();
if (fieldContainer) {
attach(fieldContainer);
lv_area_t coords;
lv_obj_get_coords(obj, &coords);
// place keyboard bellow the field with some margin
setTop(max(coords.y2 + 21, LCD_H - height()));
// save scroll position
scroll_pos = lv_obj_get_scroll_y(fieldContainer->getLvObj());
lv_obj_scroll_to_view(lvobj, LV_ANIM_OFF);
invalidate();
newField->setEditMode(true);
lv_keyboard_set_textarea(keyboard, obj);
lv_obj_add_event_cb(obj, field_focus_leave, LV_EVENT_DEFOCUSED, nullptr);
_assign_lv_group(group);
field = newField;
fieldGroup = (lv_group_t*)lv_obj_get_group(obj);
}
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (C) EdgeTX
*
* Source:
* https://github.com/edgetx/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.
*/
#pragma once
#include "window.h"
class FormField;
class Keyboard : public Window
{
public:
explicit Keyboard(coord_t height);
~Keyboard();
void clearField();
static void hide();
protected:
static Keyboard *activeKeyboard;
lv_group_t* group = nullptr;
lv_obj_t* keyboard = nullptr;
FormField* field = nullptr;
Window* fieldContainer = nullptr;
lv_group_t* fieldGroup = nullptr;
lv_coord_t scroll_pos = 0;
void setField(FormField* newField);
bool attachKeyboard();
};
+99
View File
@@ -0,0 +1,99 @@
/*
* 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 "numberedit.h"
constexpr coord_t KEYBOARD_HEIGHT = 90;
NumberKeyboard * NumberKeyboard::_instance = nullptr;
static const char* number_kb_map[] = {
"<<", "-", "+", ">>", "\n",
"MIN", "DEF", "+/-", "MAX", ""
};
#define LV_KB_BTN(width) LV_BTNMATRIX_CTRL_POPOVER | width
#define LV_KB_CTRL(width) LV_KEYBOARD_CTRL_BTN_FLAGS | width
static const lv_btnmatrix_ctrl_t number_kb_ctrl_map[] = {
LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4),
LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_CTRL(4), LV_KB_BTN(4)
};
static void on_key(lv_event_t* e)
{
lv_obj_t * obj = lv_event_get_target(e);
NumberEdit* edit = (NumberEdit*)lv_event_get_user_data(e);
if (!obj || !edit) return;
uint16_t btn_id = lv_btnmatrix_get_selected_btn(obj);
if(btn_id == LV_BTNMATRIX_BTN_NONE) return;
const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj));
if(txt == NULL) return;
if (strcmp(txt, "<<") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_BACKWARD);
} else if (strcmp(txt, "-") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_MINUS);
} else if (strcmp(txt, "+") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_PLUS);
} else if (strcmp(txt, ">>") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_FORWARD);
} else if (strcmp(txt, "MIN") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_MIN);
} else if (strcmp(txt, "DEF") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_DEFAULT);
} else if (strcmp(txt, "MAX") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_MAX);
} else if (strcmp(txt, "+/-") == 0) {
edit->onEvent(EVT_VIRTUAL_KEY_SIGN);
}
}
NumberKeyboard::NumberKeyboard() :
Keyboard(KEYBOARD_HEIGHT)
{
// setup custom keyboard
lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_USER_1,
number_kb_map, number_kb_ctrl_map);
lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_USER_1);
}
NumberKeyboard::~NumberKeyboard()
{
_instance = nullptr;
}
void NumberKeyboard::show(NumberEdit* field)
{
if (!_instance) _instance = new NumberKeyboard();
lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN);
_instance->setField(field);
auto kb = _instance->keyboard;
lv_keyboard_set_textarea(kb, nullptr);
lv_obj_remove_event_cb(kb, on_key);
lv_obj_add_event_cb(kb, on_key, LV_EVENT_VALUE_CHANGED, field);
}
+40
View File
@@ -0,0 +1,40 @@
/*
* 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.
*/
#pragma once
#include "keyboard_base.h"
class NumberEdit;
class NumberKeyboard : public Keyboard
{
public:
NumberKeyboard();
~NumberKeyboard() override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "NumberKeyboard"; }
#endif
static void show(NumberEdit* field);
protected:
static NumberKeyboard* _instance;
};
+52
View File
@@ -0,0 +1,52 @@
/*
* 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 "textedit.h"
#include "font.h"
constexpr coord_t KEYBOARD_HEIGHT = LCD_H * 2 / 5;
TextKeyboard * TextKeyboard::_instance = nullptr;
TextKeyboard::TextKeyboard() :
Keyboard(KEYBOARD_HEIGHT)
{
}
TextKeyboard::~TextKeyboard()
{
_instance = nullptr;
}
#if defined(HARDWARE_KEYS)
void TextKeyboard::onEvent(event_t event)
{
}
#endif
void TextKeyboard::show(FormField* field)
{
if (!_instance) _instance = new TextKeyboard();
lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN);
lv_keyboard_set_mode(_instance->keyboard, LV_KEYBOARD_MODE_TEXT_LOWER);
_instance->setField(field);
}
+57
View File
@@ -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.
*/
#pragma once
#include "keyboard_base.h"
#define KEYBOARD_SPACE "\t"
#define KEYBOARD_ENTER "\n"
#define KEYBOARD_BACKSPACE "\200"
#define KEYBOARD_SET_UPPERCASE "\201"
#define KEYBOARD_SET_LOWERCASE "\202"
#define KEYBOARD_SET_LETTERS "\203"
#define KEYBOARD_SET_NUMBERS "\204"
#define KEYBOARD_OFFSET_Y 15
#define KEYBOARD_ROW_HEIGHT 40
#define KEYBOARD_CHAR_WIDTH 30
#define KEYBOARD_SPACE_WIDTH 135
#define KEYBOARD_ENTER_WIDTH 80
#define KEYBOARD_BITMAP_WIDTH 45
class TextKeyboard : public Keyboard
{
public:
TextKeyboard();
~TextKeyboard() override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "TextKeyboard"; }
#endif
static void show(FormField* field);
protected:
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override;
#endif
static TextKeyboard* _instance;
};
+86
View File
@@ -0,0 +1,86 @@
/*
* 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 "layer.h"
std::list<Layer> Layer::stack;
Layer::Layer(Window* w, lv_group_t* g) : window(w), group(g) {}
Layer::~Layer() { lv_group_del(group); }
static void _assign_lv_group(lv_group_t* g)
{
lv_group_set_default(g);
// associate it with all input devices
lv_indev_t* indev = lv_indev_get_next(NULL);
while (indev) {
lv_indev_set_group(indev, g);
indev = lv_indev_get_next(indev);
}
}
void Layer::push(Window* w)
{
// create a new group
auto g = lv_group_create();
_assign_lv_group(g);
// and store
stack.emplace_back(w, g);
}
void Layer::pop(Window* w)
{
if (stack.empty()) return;
if (back() == w) {
stack.pop_back();
} else {
for (auto layer = stack.crbegin(); layer != stack.crend(); layer++) {
if (layer->window == w) {
stack.erase(layer.base());
return;
}
}
}
if (!stack.empty()) {
lv_group_t* g = stack.back().group;
_assign_lv_group(g);
} else {
lv_group_set_default(NULL);
}
}
Window* Layer::back()
{
if (stack.empty()) return nullptr;
return stack.back().window;
}
Window* Layer::getFirstOpaque()
{
for (auto layer = stack.crbegin(); layer != stack.crend(); layer++) {
auto w = layer->window;
if (w && (w->getWindowFlags() & OPAQUE)) return w;
}
return nullptr;
}
+40
View File
@@ -0,0 +1,40 @@
/*
* 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.
*/
#pragma once
#include "window.h"
class Layer
{
static std::list<Layer> stack;
Window* window;
lv_group_t* group;
public:
explicit Layer(Window* w, lv_group_t* g);
~Layer();
static void push(Window* window);
static void pop(Window* window);
static Window* back();
static Window* getFirstOpaque();
};
+21
View File
@@ -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.
*/
#pragma once
+89
View File
@@ -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.
*/
#pragma once
#include "libopenui_types.h"
#include "libopenui_compat.h"
/* obsolete flags */
#define BLINK 0
#define TIMEHOUR 0
/* drawText flags */
#define LEFT 0x00u /* align left */
// 0x01u used by Lua in api_colorlcd.h
#define VCENTERED 0x02u /* align center vertically */
#define CENTERED 0x04u /* align center */
#define RIGHT 0x08u /* align right */
#define SHADOWED 0x80u /* black copy at +1 +1 */
// 0x1000u used by Lua in api_colorlcd.h
#define SPACING_NUMBERS_CONST 0x2000u
// 0x8000u used by Lua in api_colorlcd.h
/* drawNumber flags */
#define LEADING0 0x10u
#define PREC1 0x20u
#define PREC2 0x30u
#define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4)
/* telemetry flags */
#define NO_UNIT 0x40u
#define FONT_MASK 0x0F00u
#define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8u)
#define FONT(xx) (unsigned(FONT_ ## xx ## _INDEX) << 8u)
#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 ARGB_JOIN(a, r, g, b) \
(((a&0xF) << 12) + ((r&0xF) << 8) + ((g&0xF) << 4) + (b&0xF))
#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)
#define OPACITY_MAX 0x0Fu
#define OPACITY(value) ((value) & OPACITY_MAX)
#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 COLOR2FLAGS(color) LcdFlags(unsigned(color) << 16u)
#define COLOR_VAL(flags) ((flags) >> 16u)
#define COLOR_MASK(flags) ((flags) & 0xFFFF0000u)
#define LV_OBJ_FLAG_ENCODER_ACCEL LV_OBJ_FLAG_USER_1
+57
View File
@@ -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.
*/
#pragma once
#include <inttypes.h>
#include "libopenui_config.h"
void lcdNextLayer();
uint16_t* lcdGetScratchBuffer();
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 DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t bg_color);
void onKeyError();
void killEvents(event_t event);
// multiplication factor between 0 and 25
int8_t rotaryEncoderGetAccel();
//
// These colors MUST be defined
//
// #define COLOR_THEME_SECONDARY1
// #define COLOR_THEME_SECONDARY3
// #define COLOR_THEME_PRIMARY2
// #define COLOR_THEME_FOCUS
// #define COLOR_THEME_DISABLED
// #define COLOR_THEME_ACTIVE
// #define COLOR_THEME_FOCUS
// #define COLOR_THEME_PRIMARY3
// #define COLOR_THEME_SECONDARY1
// #define COLOR_THEME_PRIMARY2
// #define COLOR_THEME_SECONDARY1_THEME_SECONDARY1
// #define COLOR_THEME_SECONDARY2
// #define COLOR_THEME_PRIMARY2
// #define COLOR_THEME_FOCUS
// #define COLOR_THEME_PRIMARY1
// #define COLOR_THEME_SECONDARY3
// #define COLOR_THEME_FOCUS
// #define CUSTOM_COLOR
+110
View File
@@ -0,0 +1,110 @@
/*
* 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 <inttypes.h>
#include <string.h>
#include "libopenui_file.h"
#include "libopenui_defines.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;
}
/**
Check if given extension exists in a list of extensions.
@param extension The extension to search for, including leading period.
@param pattern One or more file extensions concatenated together, including the periods.
The list is searched backwards and the first match, if any, is returned.
eg: ".gif.jpg.jpeg.png"
@param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1).
@retval true if a extension was found in the lost, false otherwise.
*/
bool isExtensionMatching(const char * extension, const char * pattern, char * match)
{
const char *ext;
uint8_t extlen, fnlen;
int plen;
ext = getFileExtension(pattern, 0, 0, &fnlen, &extlen);
plen = (int)fnlen;
while (plen > 0 && ext) {
if (!strncasecmp(extension, ext, extlen)) {
if (match != nullptr) strncat(&(match[0]='\0'), ext, extlen);
return true;
}
plen -= extlen;
if (plen > 0) {
ext = getFileExtension(pattern, plen, 0, nullptr, &extlen);
}
}
return false;
}
// 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;
}
+38
View File
@@ -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.
*/
#pragma once
#include "libopenui_compat.h" // for strcasecmp / VC++
#include <string>
#include <cstring>
#include <cinttypes>
#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);
// comparison, not case sensitive.
inline bool compare_nocase(const std::string & first, const std::string & second)
{
return strcasecmp(first.c_str(), second.c_str()) < 0;
}
@@ -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"
+97
View File
@@ -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.
*/
#pragma once
#include <string>
#include <cstring>
#if defined(min)
#undef min
#undef max
#endif
template<class T>
inline T min(T a, T b)
{
return a < b ? a : b;
}
template<class T>
inline T max(T a, T b)
{
return a > b ? a : b;
}
template<class T>
inline T limit(T vmin, T x, T vmax)
{
return ::min(::max(vmin, x), vmax);
}
inline int divRoundClosest(int n, int d)
{
if (d == 0)
return 0;
else
return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
}
inline int multDivRoundClosest(int v, int n, int d)
{
if (n == d)
return v;
else
return divRoundClosest(v * n, d);
}
inline int mod(int k, int n)
{
return ((k %= n) < 0) ? k + n : k;
}
inline unsigned align32(unsigned n)
{
unsigned rest = (n & 3u);
return rest ? n + 4u - rest : n;
}
template<class T>
inline T sgn(T a)
{
return a > 0 ? 1 : (a < 0 ? -1 : 0);
}
// TODO duplicated code
inline std::string TEXT_AT_INDEX(const char *const *val, uint8_t idx)
{
return std::string(val[idx]);
}
inline const char * findNextLine(const char * stack)
{
while (true) {
const char * pos = strchr(stack, '\n');
if (!pos)
return nullptr;
if (pos == stack || *((uint8_t *)(pos - 1)) < 0xFE)
return pos;
stack = pos + 1;
}
}
+75
View File
@@ -0,0 +1,75 @@
/*
* 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.
*/
#pragma once
#include <cinttypes>
typedef int coord_t;
struct point_t
{
coord_t x;
coord_t y;
bool operator != (const point_t & b) const
{
return x != b.x || y != b.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;
}
bool contains(coord_t x, coord_t y) const
{
return (x >= this->x && x < this->x + this->w && y >= this->y && y < this->y + this->h);
}
bool contains(const rect_t & other) const
{
return left() <= other.left() && right() >= other.right() && top() <= other.top() && bottom() >= other.bottom();
}
};
static const rect_t nullRect = {0, 0, 0, 0};
typedef uint32_t LcdFlags;
typedef uint16_t event_t;
+71
View File
@@ -0,0 +1,71 @@
/*
* 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"
#include "keyboard_base.h"
#include "lvgl/lvgl.h"
MainWindow * MainWindow::_instance = nullptr;
MainWindow::MainWindow() :
Window(nullptr, {0, 0, LCD_W, LCD_H}, OPAQUE), invalidatedRect(rect)
{
}
void MainWindow::emptyTrash()
{
for (auto window : trash) {
delete window;
}
trash.clear();
}
void MainWindow::invalidate(const rect_t& rect)
{
if (invalidatedRect.w) {
auto left = limit<coord_t>(0, rect.left(), invalidatedRect.left());
auto right = limit<coord_t>(invalidatedRect.right(), rect.right(), LCD_W);
auto top = limit<coord_t>(0, rect.top(), invalidatedRect.top());
auto bottom =
limit<coord_t>(rect.bottom(), invalidatedRect.bottom(), LCD_H);
invalidatedRect = {left, top, right - left, bottom - top};
} else {
invalidatedRect = rect;
}
}
void MainWindow::run(bool trash)
{
auto start = ticksNow();
auto opaque = Layer::getFirstOpaque();
if (opaque) opaque->checkEvents();
if (trash) emptyTrash();
auto delta = ticksNow() - start;
if (delta > 10 * SYSTEM_TICKS_1MS) {
TRACE_WINDOWS("MainWindow::run took %dms",
(ticksNow() - start) / SYSTEM_TICKS_1MS);
}
}
+91
View File
@@ -0,0 +1,91 @@
/*
* 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.
*/
#pragma once
#include <utility>
#include "layer.h"
#include "bitmapbuffer.h"
class MainWindow: public Window
{
protected:
// singleton
MainWindow();
public:
~MainWindow() override
{
children.clear();
}
static bool isMainWindowCreated()
{
return _instance != nullptr;
}
static MainWindow * instance()
{
if (!_instance)
_instance = new MainWindow();
return _instance;
}
#if defined(TESTS)
static void create()
{
_instance = new MainWindow();
}
#endif
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "MainWindow";
}
#endif
void setActiveScreen() {
lv_scr_load(lvobj);
}
void invalidate()
{
invalidate({0, 0, rect.w, rect.h});
}
void invalidate(const rect_t & rect) override;
bool needsRefresh() const
{
return invalidatedRect.w > 0;
}
bool refresh();
void run(bool trash=true);
protected:
static MainWindow * _instance;
static void emptyTrash();
rect_t invalidatedRect;
const char * shutdown = nullptr;
};
+389
View File
@@ -0,0 +1,389 @@
/*
* 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"
#include "font.h"
#include "theme.h"
#include <lvgl/lvgl.h>
void MenuBody::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
{
lv_canvas_t* icon = (lv_canvas_t*)lines[row].getIcon();
if (!icon) return;
lv_img_t* img = &icon->img;
lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS);
dsc->label_dsc->ofs_x = img->w + cell_left;
}
void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc)
{
if(row >= lines.size()) return;
lv_obj_t* icon = lines[row].getIcon();
if (icon) {
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_img_dsc_t* img = lv_canvas_get_img(icon);
lv_area_t coords;
lv_coord_t area_h = lv_area_get_height(dsc->draw_area);
lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS);
coords.x1 = dsc->draw_area->x1 + cell_left;
coords.x2 = coords.x1 + img->header.w - 1;
coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2;
coords.y2 = coords.y1 + img->header.h - 1;
lv_draw_img(dsc->draw_ctx, &img_dsc, &coords, img);
}
if(lines[row].isChecked != nullptr &&
lines[row].isChecked()) {
lv_area_t coords;
lv_coord_t area_h = lv_area_get_height(dsc->draw_area);
lv_coord_t cell_right = lv_obj_get_style_pad_right(lvobj, LV_PART_ITEMS);
lv_coord_t font_h = getFontHeight(FONT(STD));
coords.x1 = dsc->draw_area->x2 - cell_right - font_h;
coords.x2 = coords.x1 + font_h;
coords.y1 = dsc->draw_area->y1 + (area_h - font_h) / 2;
coords.y2 = coords.y1 + font_h - 1;
lv_draw_label(dsc->draw_ctx, dsc->label_dsc, &coords, LV_SYMBOL_OK, nullptr);
}
}
static void _force_editing(lv_group_t* g)
{
lv_group_set_editing(g, true);
}
MenuBody::MenuBody(Window * parent, const rect_t & rect):
TableField(parent, rect)
{
// Allow encoder acceleration
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL);
// Add scroll bar if needed
lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_AUTO);
setColumnCount(1);
setColumnWidth(0, rect.w);
lv_group_t* g = (lv_group_t*)lv_obj_get_group(lvobj);
if (g) {
setFocusHandler([=](bool focus) {
if (focus) {
lv_group_set_focus_cb(g, _force_editing);
} else {
lv_group_set_focus_cb(g, nullptr);
}
});
lv_group_set_editing(g, true);
}
}
void MenuBody::addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked, bool update)
{
lines.emplace_back(text, std::move(onPress), std::move(isChecked), nullptr);
if (update) {
auto idx = lines.size() - 1;
lv_table_set_cell_value(lvobj, idx, 0, text.c_str());
}
}
void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked,
bool update)
{
lv_obj_t* canvas = lv_canvas_create(nullptr);
lines.emplace_back(text, std::move(onPress), std::move(isChecked), canvas);
lv_coord_t w = *((uint16_t *)icon_mask);
lv_coord_t h = *(((uint16_t *)icon_mask)+1);
void* buf = (void*)(icon_mask + 4);
lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT);
if (update) {
auto idx = lines.size() - 1;
lv_table_set_cell_value(lvobj, idx, 0, text.c_str());
}
}
void MenuBody::updateLines()
{
setRowCount(lines.size());
for(unsigned int idx = 0; idx < lines.size(); idx++) {
lv_table_set_cell_value(lvobj, idx, 0, lines[idx].text.c_str());
}
}
void MenuBody::removeLines()
{
lines.clear();
setRowCount(0);
selectedIndex = 0;
// reset vertical scroll
lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF);
}
coord_t MenuBody::getContentHeight()
{
return lv_obj_get_self_height(lvobj);
}
void MenuBody::onPress(size_t index)
{
Menu *menu = getParentMenu();
if (index < lines.size()) {
if (menu->multiple) {
if (selectedIndex == (int)index)
lines[index].onPress();
else {
setIndex(index);
lines[index].onPress();
}
} else {
// delete menu first to avoid
// focus issues with onPress()
menu->deleteLater();
lines[index].onPress();
}
}
}
// ensure index is in range and also handle wrapping index
int MenuBody::rangeCheck(int index)
{
if (index < 0)
index = lines.size() - 1;
else if (index > (signed)lines.size() - 1)
index = 0;
return index;
}
void MenuBody::setIndex(int index)
{
if (index < (int)lines.size()) {
if (index == selectedIndex) return;
selectedIndex = index;
lv_obj_invalidate(lvobj);
lv_table_t* table = (lv_table_t*)lvobj;
if (index < 0) {
table->row_act = LV_TABLE_CELL_NONE;
table->col_act = LV_TABLE_CELL_NONE;
return;
}
table->row_act = index;
table->col_act = 0;
lv_coord_t h_before = 0;
for (uint16_t i = 0; i < table->row_act; i++)
h_before += table->row_h[i];
lv_coord_t row_h = table->row_h[table->row_act];
lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj);
lv_obj_update_layout(lvobj);
lv_coord_t h = lv_obj_get_height(lvobj);
lv_coord_t diff_y = 0;
if (h_before < scroll_y) {
diff_y = scroll_y - h_before;
} else if (scroll_y + h < h_before + row_h) {
diff_y = scroll_y + h - h_before - row_h;
} else {
return;
}
lv_obj_scroll_by_bounded(lvobj, 0, diff_y, LV_ANIM_OFF);
}
}
void MenuBody::onPress(uint16_t row, uint16_t col)
{
onPress(row);
}
void MenuBody::selectNext(MENU_DIRECTION direction)
{
// look for the next non separator line
int index = selectedIndex + direction;
setIndex(rangeCheck(index));
}
void MenuBody::onEvent(event_t event)
{
#if defined(HARDWARE_KEYS)
if (event == EVT_KEY_BREAK(KEY_EXIT)) {
onCancel();
}
else {
TableField::onEvent(event);
}
#endif
}
void MenuBody::onCancel()
{
if(_onCancel) _onCancel();
TableField::onCancel();
}
MenuWindowContent::MenuWindowContent(Menu* parent) :
ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2,
(LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}),
body(this, {0, 0, width(), height()})
{
// body.setFocus(SET_FOCUS_DEFAULT);
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
}
coord_t MenuWindowContent::getHeaderHeight() const
{
if (title) return lv_obj_get_height(title);
return 0;
}
void MenuWindowContent::deleteLater(bool detach, bool trash)
{
if (_deleted) return;
body.deleteLater(true, false);
ModalWindowContent::deleteLater(detach, trash);
}
Menu::Menu(Window * parent, bool multiple):
ModalWindow(parent, true),
content(createMenuWindow(this)),
multiple(multiple)
{
}
void Menu::setToolbar(Window * window)
{
toolbar = window;
updatePosition();
toolbar->setLeft(content->left() - toolbar->width());
toolbar->setTop(content->top());
toolbar->setHeight(content->height());
}
void Menu::setOutline(Window* obj)
{
lv_obj_set_style_outline_width(obj->getLvObj(), 2, LV_PART_MAIN);
lv_obj_set_style_outline_color(obj->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN);
lv_obj_set_style_outline_opa(obj->getLvObj(), LV_OPA_100, LV_PART_MAIN);
lv_obj_set_style_outline_pad(obj->getLvObj(), 1, LV_PART_MAIN);
}
void Menu::updatePosition()
{
coord_t height = content->body.getContentHeight();
if (toolbar) {
content->setLeft((LCD_W - content->width() + toolbar->width())/2);
setOutline(toolbar);
} else {
content->setLeft((LCD_W - content->width())/2);
auto headerHeight = content->getHeaderHeight();
auto bodyHeight = min<coord_t>(height, MENUS_MAX_HEIGHT - headerHeight);
content->setTop((LCD_H - headerHeight - bodyHeight) / 2);
content->setHeight(headerHeight + bodyHeight);
content->body.setTop(headerHeight);
content->body.setHeight(bodyHeight);
}
setOutline(content);
}
void Menu::setTitle(std::string text)
{
content->setTitle(std::move(text));
updatePosition();
}
void Menu::addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(text, std::move(onPress), std::move(isChecked));
updatePosition();
}
void Menu::addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(icon_mask, text, std::move(onPress), std::move(isChecked));
updatePosition();
}
void Menu::addLineBuffered(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(text, std::move(onPress), std::move(isChecked), false);
}
void Menu::addLineBuffered(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked)
{
content->body.addLine(icon_mask, text, std::move(onPress), std::move(isChecked), false);
}
void Menu::updateLines()
{
content->body.updateLines();
updatePosition();
}
void Menu::removeLines()
{
content->body.removeLines();
updatePosition();
}
void Menu::onEvent(event_t event)
{
#if defined(HARDWARE_KEYS)
if (toolbar &&
(event == EVT_KEY_BREAK(KEY_PGDN) ||
#if defined(KEYS_GPIO_REG_PGUP)
event == EVT_KEY_BREAK(KEY_PGUP)
#else
event == EVT_KEY_LONG(KEY_PGDN)
#endif
)) {
toolbar->onEvent(event);
}
#endif
}
void Menu::onCancel()
{
deleteLater();
}
+245
View File
@@ -0,0 +1,245 @@
/*
* 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.
*/
#pragma once
#include <vector>
#include <functional>
#include <utility>
#include <memory>
#include "modal_window.h"
#include "table.h"
class Menu;
class MenuWindowContent;
struct lvobj_delete {
constexpr lvobj_delete() = default;
void operator()(lv_obj_t* obj) const {
lv_obj_del(obj);
}
};
class MenuBody: public TableField
{
friend class MenuWindowContent;
friend class Menu;
enum MENU_DIRECTION
{
DIRECTION_UP = 1,
DIRECTION_DOWN = -1
};
class MenuLine
{
friend class MenuBody;
public:
MenuLine(const std::string& text, std::function<void()> onPress, std::function<bool()> isChecked,
lv_obj_t *icon) :
text(text), onPress(std::move(onPress)), isChecked(std::move(isChecked)), icon(icon)
{
}
MenuLine(MenuLine &) = delete;
MenuLine(MenuLine &&) = default;
lv_obj_t* getIcon() { return icon.get(); }
protected:
std::string text;
std::function<void()> onPress;
std::function<bool()> isChecked;
std::unique_ptr<lv_obj_t, lvobj_delete> icon;
};
public:
MenuBody(Window * parent, const rect_t & rect);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "MenuBody";
}
#endif
void setIndex(int index);
int selection() const
{
return selectedIndex;
}
int count() const
{
return lines.size();
}
void onEvent(event_t event) override;
void onCancel() override;
void addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked, bool update = true);
void addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked,
bool update = true);
void updateLines();
void removeLines();
void setCancelHandler(std::function<void()> handler)
{
_onCancel = std::move(handler);
}
coord_t getContentHeight();
void onPress(size_t index);
protected:
void onPress(uint16_t row, uint16_t col) override;
void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override;
void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override;
void selectNext(MENU_DIRECTION direction);
int rangeCheck(int);
std::vector<MenuLine> lines;
int selectedIndex = 0;
std::function<void()> _onCancel;
inline Menu * getParentMenu();
};
class MenuWindowContent: public ModalWindowContent
{
friend class Menu;
public:
explicit MenuWindowContent(Menu * parent);
coord_t getHeaderHeight() const;
void deleteLater(bool detach = true, bool trash = true) override;
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "MenuWindowContent";
}
#endif
protected:
MenuBody body;
};
class Menu: public ModalWindow
{
friend class MenuBody;
public:
explicit Menu(Window * parent, bool multiple = false);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Menu";
}
#endif
void setCancelHandler(std::function<void()> handler)
{
content->body.setCancelHandler(std::move(handler));
}
void setWaitHandler(std::function<void()> handler)
{
waitHandler = std::move(handler);
}
void setToolbar(Window* window);
void setTitle(std::string text);
void addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLine(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLineBuffered(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void addLineBuffered(const uint8_t *icon_mask, const std::string &text,
std::function<void()> onPress,
std::function<bool()> isChecked = nullptr);
void updateLines();
void addSeparator();
void removeLines();
unsigned count() const
{
return content->body.count();
}
int selection() const
{
return content->body.selection();
}
void select(int index)
{
content->body.setIndex(index);
}
void onEvent(event_t event) override;
void onCancel() override;
void checkEvents() override
{
ModalWindow::checkEvents();
if (waitHandler) {
waitHandler();
}
}
protected:
MenuWindowContent* content;
bool multiple;
Window * toolbar = nullptr;
std::function<void()> waitHandler;
void updatePosition();
void setOutline(Window* obj);
};
Menu * MenuBody::getParentMenu()
{
return static_cast<Menu *>(getParent()->getParent());
}
+163
View File
@@ -0,0 +1,163 @@
/*
* 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 "choice.h"
#include "font.h"
static void toolbar_btn_defocus(lv_event_t* event)
{
auto obj = lv_event_get_target(event);
auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj);
btn->check(false);
}
MenuToolbarButton::MenuToolbarButton(Window* parent, const rect_t& rect,
const char* picto) :
Button(parent, rect, nullptr, 0, 0, window_create), picto(picto)
{
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
lv_obj_add_event_cb(lvobj, toolbar_btn_defocus, LV_EVENT_DEFOCUSED, nullptr);
}
void MenuToolbarButton::paint(BitmapBuffer * dc)
{
lv_coord_t width = lv_obj_get_content_width(lvobj);
if (checked()) {
dc->drawSolidFilledRect(
MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING,
width - 2 * MENUS_TOOLBAR_BUTTON_PADDING,
MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING,
COLOR_THEME_FOCUS);
dc->drawText(width / 2, (rect.h - getFontHeight(FONT(L))) / 2 + 2,
picto, FONT(L) | CENTERED | COLOR_THEME_PRIMARY2);
} else {
dc->drawText(width / 2, (rect.h - getFontHeight(FONT(L))) / 2 + 2,
picto, FONT(L) | CENTERED | COLOR_THEME_PRIMARY1);
}
}
MenuToolbar::MenuToolbar(Choice* choice, Menu* menu) :
Window(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR),
choice(choice),
menu(menu),
group(lv_group_create())
{
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
lv_group_add_obj(group, lvobj);
}
MenuToolbar::~MenuToolbar()
{
lv_group_del(group);
}
void MenuToolbar::resetFilter()
{
if (lv_group_get_focused(group) != lvobj) {
lv_group_focus_obj(lvobj);
choice->fillMenu(menu);
}
}
void MenuToolbar::onEvent(event_t event)
{
if (event == EVT_KEY_BREAK(KEY_PGDN)) {
lv_group_focus_next(group);
}
#if defined(KEYS_GPIO_REG_PGUP)
else if (event == EVT_KEY_BREAK(KEY_PGUP)) {
#else
else if (event == EVT_KEY_LONG(KEY_PGDN)) {
killEvents(event);
#endif
lv_group_focus_prev(group);
}
auto obj = lv_group_get_focused(group);
if (!obj) {
choice->fillMenu(menu);
} else {
lv_event_send(obj, LV_EVENT_CLICKED, nullptr);
}
}
rect_t MenuToolbar::getButtonRect(size_t buttons)
{
coord_t y = buttons * (MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING);
return {0, y, LV_PCT(100), MENUS_TOOLBAR_BUTTON_WIDTH};
}
bool MenuToolbar::filterMenu(MenuToolbarButton* btn, int16_t filtermin,
int16_t filtermax)
{
btn->check(!btn->checked());
Choice::FilterFct filter = nullptr;
if (btn->checked()) {
filter = [=](int16_t index) {
return index >= filtermin && index <= filtermax;
};
}
choice->fillMenu(menu, filter);
return btn->checked();
}
typedef std::function<bool(int)> IsValueAvailable;
static int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable)
{
int retval = 0;
for (int i = min; i <= max; i++) {
if (isValueAvailable(i)) {
retval = i;
break;
}
}
return retval;
}
void MenuToolbar::addButton(const 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;
rect_t r = getButtonRect(children.size());
auto button = new MenuToolbarButton(this, r, picto);
button->setPressHandler(
std::bind(&MenuToolbar::filterMenu, this, button, filtermin, filtermax));
lv_group_add_obj(group, button->getLvObj());
}
void MenuToolbar::onClicked()
{
choice->fillMenu(menu);
}
+60
View File
@@ -0,0 +1,60 @@
/*
* 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.
*/
#pragma once
#include "button.h"
#include "menu.h"
class Choice;
class MenuToolbarButton : public Button
{
public:
MenuToolbarButton(Window* parent, const rect_t& rect, const char* picto);
void paint(BitmapBuffer* dc) override;
protected:
const char* picto;
};
class MenuToolbar : public Window
{
// friend Menu;
public:
MenuToolbar(Choice* choice, Menu* menu);
~MenuToolbar();
void resetFilter();
void onEvent(event_t event) override;
protected:
Choice* choice;
Menu* menu;
lv_group_t* group;
void addButton(const char* picto, int16_t filtermin, int16_t filtermax);
bool filterMenu(MenuToolbarButton* btn, int16_t filtermin, int16_t filtermax);
rect_t getButtonRect(size_t buttons);
void onClicked() override;
};
+84
View File
@@ -0,0 +1,84 @@
/*
* 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 "modal_window.h"
#include "keyboard_base.h"
#include "font.h"
#include "layer.h"
ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside):
Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}),
closeWhenClickOutside(closeWhenClickOutside)
{
Layer::push(this);
lv_obj_set_style_bg_color(lvobj, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(lvobj, LV_OPA_50, LV_PART_MAIN);
}
void ModalWindow::deleteLater(bool detach, bool trash)
{
if (_deleted) return;
Layer::pop(this);
Window::deleteLater(detach, trash);
}
void ModalWindow::onClicked()
{
if (closeWhenClickOutside) {
deleteLater();
}
}
ModalWindowContent::ModalWindowContent(ModalWindow* parent,
const rect_t& rect) :
Window(parent, rect, OPAQUE)
{
}
void ModalWindowContent::onClicked()
{
Keyboard::hide();
}
void ModalWindowContent::setTitle(const std::string& text)
{
if (!title) {
title = lv_label_create(lvobj);
lv_obj_move_to_index(title, 0);
lv_obj_set_width(title, lv_pct(100));
lv_obj_set_style_pad_all(title, PAGE_PADDING, LV_PART_MAIN);
lv_color_t bg_color = makeLvColor(COLOR_THEME_SECONDARY1);
lv_obj_set_style_bg_color(title, bg_color, LV_PART_MAIN);
lv_obj_set_style_bg_opa(title, LV_OPA_100, LV_PART_MAIN);
lv_color_t txt_color = makeLvColor(COLOR_THEME_PRIMARY2);
lv_obj_set_style_text_color(title, txt_color, LV_PART_MAIN);
lv_obj_update_layout(lvobj);
}
lv_label_set_text(title, text.c_str());
}
std::string ModalWindowContent::getTitle() const
{
if (!title) return std::string();
return lv_label_get_text(title);
}
+61
View File
@@ -0,0 +1,61 @@
/*
* 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.
*/
#pragma once
#include "window.h"
class ModalWindow : public Window
{
public:
explicit ModalWindow(Window* parent, bool closeWhenClickOutside = false);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "ModalWindow"; }
#endif
void setCloseWhenClickOutside(bool value = true)
{
closeWhenClickOutside = value;
}
void onClicked() override;
void deleteLater(bool detach = true, bool trash = true) override;
protected:
bool closeWhenClickOutside;
};
class ModalWindowContent : public Window
{
public:
explicit ModalWindowContent(ModalWindow* parent, const rect_t& rect);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "ModalWindowContent"; }
#endif
virtual void setTitle(const std::string& text);
std::string getTitle() const;
void onClicked() override;
protected:
lv_obj_t* title = nullptr;
};
+154
View File
@@ -0,0 +1,154 @@
/*
* 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"
#include "widgets/field_edit.h"
#include "keyboard_number.h"
static void numberedit_cb(lv_event_t* e)
{
NumberEdit* numEdit = (NumberEdit*)lv_event_get_user_data(e);
if (!numEdit || numEdit->deleted()) return;
uint32_t key = lv_event_get_key(e);
switch (key) {
case LV_KEY_LEFT:
numEdit->onEvent(EVT_ROTARY_LEFT);
break;
case LV_KEY_RIGHT:
numEdit->onEvent(EVT_ROTARY_RIGHT);
break;
}
}
NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax,
std::function<int()> getValue,
std::function<void(int)> setValue,
WindowFlags windowFlags, LcdFlags textFlags) :
BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue),
std::move(setValue), windowFlags, textFlags,
field_edit_create)
{
// Allow encoder acceleration
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL);
// properties
lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF);
lv_textarea_set_password_mode(lvobj, false);
lv_textarea_set_one_line(lvobj, true);
lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this);
setHeight(33);
padTop(5);
padRight(4);
lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_RIGHT, 0);
lv_obj_set_style_radius(lvobj, 4, 0);
update();
}
void NumberEdit::onEvent(event_t event)
{
TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event);
if (editMode) {
switch (event) {
#if defined(HARDWARE_KEYS)
case EVT_ROTARY_RIGHT: {
int value = getValue();
auto step = getStep();
step += (rotaryEncoderGetAccel() * getAccelFactor()) / 8;
do {
value += step;
} while (isValueAvailable && !isValueAvailable(value) && value <= vmax);
if (value <= vmax) {
setValue(value);
}
else {
setValue(vmax);
onKeyError();
}
return;
}
case EVT_ROTARY_LEFT: {
int value = getValue();
auto step = getStep();
step += (rotaryEncoderGetAccel() * getAccelFactor()) / 8;
do {
value -= step;
} while (isValueAvailable && !isValueAvailable(value) && value >= vmin);
if (value >= vmin) {
setValue(value);
}
else {
setValue(vmin);
onKeyError();
}
return;
}
#endif
case EVT_VIRTUAL_KEY_PLUS:
setValue(getValue() + getStep());
break;
case EVT_VIRTUAL_KEY_MINUS:
setValue(getValue() - getStep());
break;
case EVT_VIRTUAL_KEY_FORWARD:
setValue(getValue() + getFastStep() * getStep());
break;
case EVT_VIRTUAL_KEY_BACKWARD:
setValue(getValue() - getFastStep() * getStep());
break;
case EVT_VIRTUAL_KEY_DEFAULT:
setValue(getDefault());
break;
case EVT_VIRTUAL_KEY_MAX:
setValue(getMax());
break;
case EVT_VIRTUAL_KEY_MIN:
setValue(getMin());
break;
case EVT_VIRTUAL_KEY_SIGN:
setValue(-getValue());
break;
}
}
FormField::onEvent(event);
}
void NumberEdit::onClicked()
{
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
if(indev_type == LV_INDEV_TYPE_POINTER) {
NumberKeyboard::show(this);
return;
}
FormField::onClicked();
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.
*/
#pragma once
#include "basenumberedit.h"
#include <string>
class NumberEdit : public BaseNumberEdit
{
public:
NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax,
std::function<int()> getValue,
std::function<void(int)> setValue = nullptr,
WindowFlags windowFlags = 0, LcdFlags textFlags = 0);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "NumberEdit(" + std::to_string(getValue()) + ")";
}
#endif
void setAvailableHandler(std::function<bool(int)> handler)
{
isValueAvailable = std::move(handler);
}
void onEvent(event_t event) override;
void onClicked() override;
protected:
std::function<bool(int)> isValueAvailable;
};
+27
View File
@@ -0,0 +1,27 @@
/*
* 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)
{
auto val = value > 100 ? 100 : value;
theme->drawProgressBar(dc, 0, 0, width(), height(), val, 100);
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.
*/
#pragma once
#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() const override
{
return "Progress";
}
#endif
void setValue(int newValue)
{
if (newValue != value) {
value = newValue;
invalidate();
}
}
void paint(BitmapBuffer * dc) override;
protected:
int value = 0;
};
+175
View File
@@ -0,0 +1,175 @@
/*
* 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.
*/
#pragma once
#include <touch.h>
#include <choice.h>
#include <font.h>
#include <static.h>
#include <limits.h>
#include "libopenui_config.h"
constexpr WindowFlags ROLLER_SEPARATION_LINES = (FORM_FLAGS_LAST << 1u);
constexpr coord_t SCROLL_POSITION_INVALIDATED = INT_MIN;
class Roller: public Choice
{
public:
Roller(FormGroup * parent, const rect_t & rect, const char * label, const char * const * values, int vmin, int vmax, std::function<int()> getValue, std::function<void(int)> setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES):
Choice(parent, rect, values, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR)
{
init(label);
}
Roller(FormGroup * parent, const rect_t & rect, const char * label, std::vector<std::string> values, int vmin, int vmax, std::function<int()> getValue, std::function<void(int)> setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES):
Choice(parent, rect, std::move(values), vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR)
{
init(label);
}
void init(const char * label)
{
if (label) {
// the label is another window, could be changed, but is it needed?
new StaticText(parent, {rect.x, rect.y - ROLLER_LINE_HEIGHT, rect.w, ROLLER_LINE_HEIGHT}, label, 0, CENTERED);
}
setHeight(ROLLER_LINE_HEIGHT * 3 - 1);
setPageHeight(ROLLER_LINE_HEIGHT);
setInnerHeight(INFINITE_HEIGHT);
updateScrollPositionFromValue();
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Roller";
}
#endif
void paint(BitmapBuffer * dc) override
{
int32_t value = getValue();
auto valuesCount = (int)getValuesCount();
if (valuesCount == 0) {
return;
}
int index = (scrollPositionY - ROLLER_LINE_HEIGHT + 1) / ROLLER_LINE_HEIGHT;
coord_t y = index * ROLLER_LINE_HEIGHT;
coord_t yMax = scrollPositionY + 3 * ROLLER_LINE_HEIGHT;
index = mod(index, valuesCount);
while (y < yMax) {
auto displayedValue = getValueFromIndex(index);
auto fgColor = COLOR_THEME_DISABLED;
if (value == displayedValue) {
fgColor = COLOR_THEME_PRIMARY2 | FONT(STD);
}
unsigned valueIndex = displayedValue - vmin;
if (valueIndex < values.size()) {
dc->drawText(width() / 2, y, values[valueIndex].c_str(), fgColor | CENTERED);
}
else {
dc->drawNumber(width() / 2, y, displayedValue, fgColor | CENTERED);
}
if (windowFlags & ROLLER_SEPARATION_LINES) {
dc->drawSolidHorizontalLine(0, y - 10, width(), COLOR_THEME_DISABLED);
}
if (++index == valuesCount)
index = 0;
y += ROLLER_LINE_HEIGHT;
}
}
void checkEvents() override
{
Window::checkEvents();
if (lastEditMode != editMode) {
updateScrollPositionFromValue();
lastEditMode = editMode;
}
if (lastScrollPositionY == SCROLL_POSITION_INVALIDATED) {
updateScrollPositionFromValue();
}
#if defined(HARDWARE_TOUCH)
if (touchState.event != TE_SLIDE) {
if (scrollPositionY != lastScrollPositionY) {
updateValueFromScrollPosition();
}
}
#endif
}
// virtual not needed until now
void setMin(int value)
{
Choice::setMin(value);
invalidateScrollPosition();
}
// virtual not needed until now
void setMax(int value)
{
Choice::setMax(value);
invalidateScrollPosition();
}
// virtual not needed until now
void setAvailableHandler(std::function<bool(int)> handler)
{
Choice::setAvailableHandler(std::move(handler));
invalidateScrollPosition();
}
void invalidateScrollPosition()
{
lastScrollPositionY = SCROLL_POSITION_INVALIDATED;
}
protected:
coord_t lastScrollPositionY = SCROLL_POSITION_INVALIDATED;
bool lastEditMode = false;
void updateScrollPositionFromValue()
{
setScrollPositionY(ROLLER_LINE_HEIGHT * (getIndexFromValue(this->getValue()) - 1));
lastScrollPositionY = scrollPositionY;
}
void updateValueFromScrollPosition()
{
lastScrollPositionY = scrollPositionY;
auto valuesCount = getValuesCount();
auto newValue = getValueFromIndex(mod((scrollPositionY / ROLLER_LINE_HEIGHT) + 1, valuesCount));
setValue(newValue);
invalidate();
}
};
+103
View File
@@ -0,0 +1,103 @@
/*
* 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"
#include "LvglWrapper.h"
static void slider_changed_cb(lv_event_t* e)
{
lv_obj_t* target = lv_event_get_target(e);
auto code = lv_event_get_code(e);
Slider* sl = (Slider*)lv_obj_get_user_data(target);
if (code == LV_EVENT_VALUE_CHANGED) {
if (sl != nullptr) sl->setValue(lv_slider_get_value(target));
}
}
static lv_style_t style_main;
static lv_style_t style_ind;
static lv_style_t style_knob;
static lv_style_t style_focused;
static lv_style_t style_edit;
constexpr coord_t SLIDER_PADDING = 6;
Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax,
std::function<int()> getValue,
std::function<void(int)> setValue) :
FormField(parent,
{rect.x + SLIDER_PADDING, rect.y + SLIDER_PADDING,
rect.w - 2 * SLIDER_PADDING, rect.h - 2 * SLIDER_PADDING},
0, 0, lv_slider_create),
vmin(vmin),
vmax(vmax),
_getValue(std::move(getValue)),
_setValue(std::move(setValue))
{
lv_obj_add_event_cb(lvobj, slider_changed_cb, LV_EVENT_VALUE_CHANGED, this);
lv_obj_add_event_cb(lvobj, slider_changed_cb, LV_EVENT_PRESSED, this);
lv_slider_set_range(lvobj, vmin, vmax);
if (_getValue != nullptr)
lv_slider_set_value(lvobj, _getValue(), LV_ANIM_OFF);
// LV_PART_MAIN
lv_style_init(&style_main);
lv_style_set_bg_opa(&style_main, LV_OPA_100);
lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1));
lv_style_set_radius(&style_main, LV_RADIUS_CIRCLE);
lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN);
// LV_PART_INDICATOR
lv_style_init(&style_ind);
lv_style_set_bg_opa(&style_ind, LV_OPA_100);
lv_style_set_bg_color(&style_ind, makeLvColor(COLOR_THEME_SECONDARY1));
lv_obj_add_style(lvobj, &style_ind, LV_PART_INDICATOR);
// Knob
lv_style_init(&style_knob);
lv_style_set_bg_color(&style_knob, makeLvColor(COLOR_THEME_PRIMARY2));
lv_style_set_border_color(&style_knob, makeLvColor(COLOR_THEME_SECONDARY1));
lv_style_set_border_width(&style_knob, 2);
lv_obj_add_style(lvobj, &style_knob, LV_PART_KNOB);
// LV_STATE_FOCUSED
lv_style_init(&style_focused);
lv_style_set_bg_color(&style_focused, makeLvColor(COLOR_THEME_FOCUS));
lv_style_set_outline_width(&style_focused, 0);
lv_obj_add_style(lvobj, &style_focused, LV_PART_MAIN | LV_STATE_FOCUSED);
lv_obj_add_style(lvobj, &style_focused, LV_PART_MAIN | LV_STATE_FOCUSED | LV_STATE_EDITED);
lv_obj_add_style(lvobj, &style_focused, LV_PART_INDICATOR | LV_STATE_FOCUSED);
lv_obj_add_style(lvobj, &style_focused, LV_PART_INDICATOR | LV_STATE_FOCUSED | LV_STATE_EDITED);
lv_style_init(&style_edit);
lv_style_set_bg_color(&style_edit, makeLvColor(COLOR_THEME_EDIT));
lv_obj_add_style(lvobj, &style_edit, LV_PART_KNOB | LV_STATE_FOCUSED | LV_STATE_EDITED);
}
int Slider::value(coord_t x) const
{
return vmin + ((vmax - vmin) * x + (rect.w / 2)) / rect.w;
}
+46
View File
@@ -0,0 +1,46 @@
/*
* 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.
*/
#pragma once
#include "form.h"
class Slider : public FormField
{
public:
Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax,
std::function<int()> getValue, std::function<void(int)> setValue);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "Slider"; }
#endif
void setValue(int value)
{
if (_setValue != nullptr) _setValue(limit(vmin, value, vmax));
}
protected:
int vmin;
int vmax;
std::function<int()> _getValue;
std::function<void(int)> _setValue;
int value(coord_t x) const;
};
+161
View File
@@ -0,0 +1,161 @@
/*
* 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"
StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt,
WindowFlags windowFlags, LcdFlags textFlags) :
Window(parent, rect, windowFlags, textFlags, lv_label_create),
text(std::move(txt))
{
lv_obj_enable_style_refresh(false);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_set_style_text_font(lvobj, getFont(textFlags), LV_PART_MAIN);
lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN);
if (textFlags & CENTERED)
lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_CENTER, 0);
else if (textFlags & RIGHT)
lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_RIGHT, 0);
if (windowFlags & BUTTON_BACKGROUND) {
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN);
setBackgroundColor(COLOR_THEME_SECONDARY2);
}
lv_obj_set_style_grid_cell_x_align(lvobj, LV_GRID_ALIGN_STRETCH, 0);
lv_label_set_text(lvobj, text.c_str());
lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
lv_obj_enable_style_refresh(true);
lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY);
}
#if defined(DEBUG_WINDOWS)
std::string StaticText::getName() const
{
return "StaticText \"" + text + "\"";
}
#endif
void StaticText::setText(std::string value)
{
if (text != value) {
text = std::move(value);
if (lvobj) lv_label_set_text(lvobj, text.c_str());
}
}
const std::string& StaticText::getText() const
{
return text;
}
void StaticText::setBackgroundColor(LcdFlags color)
{
bgColor = color;
if (!lvobj) return;
lv_color_t c = makeLvColor(bgColor);
lv_obj_set_style_bg_color(lvobj, c, LV_PART_MAIN);
}
void StaticText::setBackgroudOpacity(uint32_t opa)
{
lv_obj_set_style_bg_opa(lvobj, opa, LV_PART_MAIN);
}
void StaticText::setFont(LcdFlags font)
{
lv_obj_set_style_text_font(lvobj, getFont(font), 0);
}
template <>
void DynamicNumber<uint32_t>::updateText()
{
if (lvobj) {
const char* p = prefix ? prefix : "";
const char* s = suffix ? suffix : "";
if ((textFlags & PREC2) == PREC2) {
lv_label_set_text_fmt(lvobj, "%s%" PRIu32 ".%02" PRIu32 "%s", p,
value / 100, value % 100, s);
} else if (textFlags & PREC1) {
lv_label_set_text_fmt(lvobj, "%s%" PRIu32 ".%01" PRIu32 "%s", p,
value / 10, value % 10, s);
} else {
lv_label_set_text_fmt(lvobj, "%s%" PRIu32 "%s", p, value, s);
}
}
}
template <>
void DynamicNumber<int32_t>::updateText()
{
if (lvobj) {
const char* p = prefix ? prefix : "";
const char* s = suffix ? suffix : "";
if ((textFlags & PREC2) == PREC2) {
lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%02" PRIu32 "%s", p,
value / 100, (uint32_t)abs(value % 100), s);
} else if (textFlags & PREC1) {
lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%01" PRIu32 "%s", p,
value / 10, (uint32_t)abs(value % 10), s);
} else {
lv_label_set_text_fmt(lvobj, "%s%" PRId32 "%s", p, value, s);
}
}
}
template <>
void DynamicNumber<uint16_t>::updateText()
{
if (lvobj) {
const char* p = prefix ? prefix : "";
const char* s = suffix ? suffix : "";
if ((textFlags & PREC2) == PREC2) {
lv_label_set_text_fmt(lvobj, "%s%" PRIu16 ".%02" PRIu16 "%s", p,
(uint16_t)(value / 100), (uint16_t)(value % 100), s);
} else if (textFlags & PREC1) {
lv_label_set_text_fmt(lvobj, "%s%" PRIu16 ".%01" PRIu16 "%s", p,
(uint16_t)(value / 10), (uint16_t)(value % 10), s);
} else {
lv_label_set_text_fmt(lvobj, "%s%" PRIu16 "%s", p, value, s);
}
}
}
template <>
void DynamicNumber<int16_t>::updateText()
{
if (lvobj) {
const char* p = prefix ? prefix : "";
const char* s = suffix ? suffix : "";
if ((textFlags & PREC2) == PREC2) {
lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%02" PRIu16 "%s", p,
(int16_t)(value / 100), (uint16_t)abs(value % 100), s);
} else if (textFlags & PREC1) {
lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%01" PRIu16 "%s", p,
(int16_t)(value / 10), (uint16_t)abs(value % 10), s);
} else {
lv_label_set_text_fmt(lvobj, "%s%" PRId16 "%s", p, value, s);
}
}
}
+198
View File
@@ -0,0 +1,198 @@
/*
* 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.
*/
#pragma once
#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 = "",
WindowFlags windowFlags = 0, LcdFlags textFlags = 0);
#if defined(DEBUG_WINDOWS)
std::string getName() const override;
#endif
void setText(std::string value);
const std::string& getText() const;
void setBackgroundColor(LcdFlags color);
void setBackgroudOpacity(uint32_t opa);
void setFont(LcdFlags font);
protected:
std::string text;
LcdFlags bgColor = 0;
};
class Subtitle: public StaticText {
public:
Subtitle(Window * parent, const rect_t & rect, const char * text, WindowFlags windowFlags = 0, LcdFlags textFlags = 0):
StaticText(parent, rect, text, windowFlags, textFlags | FONT(BOLD))
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Subtitle \"" + text + "\"";
}
#endif
};
class StaticBitmap: public Window
{
public:
StaticBitmap(Window * parent, const rect_t & rect, bool scale = false):
Window(parent, rect),
scale(scale)
{
}
StaticBitmap(Window * parent, const rect_t & rect, const char * filename, bool scale = false):
Window(parent, rect),
bitmap(BitmapBuffer::loadBitmap(filename)),
scale(scale)
{
}
StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap, bool scale = false):
Window(parent, rect),
bitmap(bitmap),
scale(scale)
{
}
StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * mask, LcdFlags color, bool scale = false):
Window(parent, rect),
bitmap(mask),
color(color),
scale(scale)
{
}
void setBitmap(const char * filename)
{
setBitmap(BitmapBuffer::loadBitmap(filename));
}
void setMaskColor(LcdFlags value)
{
color = value;
}
void setBitmap(const BitmapBuffer * newBitmap)
{
delete bitmap;
bitmap = newBitmap;
invalidate();
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "StaticBitmap";
}
#endif
void paint(BitmapBuffer * dc) override
{
if (bitmap) {
if (color != 0xFFFFFFFF)
dc->drawMask(0, 0, bitmap, color);
else if (scale)
dc->drawScaledBitmap(bitmap, 0, 0, width(), height());
else
dc->drawBitmap((width() - bitmap->width()) / 2, (height() - bitmap->height()) / 2, bitmap);
}
}
protected:
const BitmapBuffer * bitmap = nullptr;
LcdFlags color = 0xFFFFFFFF;
bool scale = false;
};
class DynamicText : public StaticText
{
public:
DynamicText(Window *parent, const rect_t &rect,
std::function<std::string()> textHandler,
LcdFlags textFlags = 0) :
StaticText(parent, rect, "", 0, textFlags),
textHandler(std::move(textHandler))
{
}
void checkEvents() override
{
StaticText::checkEvents();
setText(textHandler());
}
protected:
std::function<std::string()> textHandler;
};
template <class T>
class DynamicNumber : public StaticText
{
public:
DynamicNumber(Window *parent, const rect_t &rect,
std::function<T()> numberHandler, LcdFlags textFlags = 0,
const char *prefix = nullptr, const char *suffix = nullptr) :
StaticText(parent, rect, "", 0, textFlags),
numberHandler(std::move(numberHandler)),
prefix(prefix),
suffix(suffix)
{
updateText();
}
void checkEvents() override
{
T newValue = numberHandler();
if (value != newValue) {
value = newValue;
updateText();
}
}
void setPrefix(const char * value) {
prefix = value;
updateText();
}
void setSuffix(const char * value) {
suffix = value;
updateText();
}
protected:
T value = 0;
std::function<T()> numberHandler;
const char *prefix;
const char *suffix;
void updateText();
};
+270
View File
@@ -0,0 +1,270 @@
/*
* 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 "table.h"
#include "widgets/edgetx_table.h"
void TableField::event_cb(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_VALUE_CHANGED) {
clicked(e);
} else if (code == LV_EVENT_DRAW_PART_BEGIN) {
draw_begin(e);
} else if (code == LV_EVENT_DRAW_PART_END) {
draw_end(e);
}
else if (code == LV_EVENT_DRAW_POST) {
lv_obj_t * obj = lv_event_get_target(e);
bool has_focus = lv_obj_has_state(obj, LV_STATE_FOCUS_KEY);
bool is_edited = lv_group_get_editing((lv_group_t*)lv_obj_get_group(obj));
if (has_focus && !is_edited) {
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
rect_dsc.bg_opa = LV_OPA_TRANSP;
rect_dsc.bg_img_opa = LV_OPA_TRANSP;
rect_dsc.outline_opa = LV_OPA_TRANSP;
rect_dsc.shadow_opa = LV_OPA_TRANSP;
rect_dsc.border_color = makeLvColor(COLOR_THEME_FOCUS);
rect_dsc.border_opa = LV_OPA_100;
rect_dsc.border_width = lv_dpx(2);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
lv_draw_rect(draw_ctx, &rect_dsc, &coords);
}
}
}
void TableField::clicked(lv_event_t *e)
{
lv_obj_t* target = lv_event_get_target(e);
if (!target) return;
TableField* tf = (TableField*)lv_obj_get_user_data(target);
if (!tf) return;
uint16_t row;
uint16_t col;
lv_table_get_selected_cell(target, &row, &col);
if (row == LV_TABLE_CELL_NONE || col == LV_TABLE_CELL_NONE)
return;
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
if(indev_type == LV_INDEV_TYPE_ENCODER) {
// Encoder send VALUE_CHANGED when selection changes
tf->adjustScroll();
tf->onSelected(row, col);
} else {
// Otherwise it's a click
if (lv_group_get_editing((lv_group_t*)lv_obj_get_group(target))
|| indev_type == LV_INDEV_TYPE_POINTER) {
tf->onPress(row, col);
} else {
tf->onClicked();
}
// Note: VALUE_CHANGED is generated on RELEASED
//
// It must be avoided that CLICKED be generated afterwards
// in case the object has been deleted meanwhile and to
// avoid onClicked() being called.
//
lv_indev_reset(lv_indev_get_act(), nullptr);
}
}
void TableField::draw_begin(lv_event_t* e)
{
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
if (dsc->part != LV_PART_ITEMS) return;
lv_obj_t* table = lv_event_get_target(e);
if (!table) return;
uint16_t cols = lv_table_get_col_cnt(table);
if (cols == 0) return;
TableField* tf = (TableField*)lv_obj_get_user_data(table);
if (!tf) return;
uint16_t row = dsc->id / cols;
uint16_t col = dsc->id % cols;
tf->onDrawBegin(row, col, dsc);
}
void TableField::draw_end(lv_event_t* e)
{
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
if (dsc->part != LV_PART_ITEMS) return;
lv_obj_t* table = lv_event_get_target(e);
if (!table) return;
uint16_t cols = lv_table_get_col_cnt(table);
if (cols == 0) return;
TableField* tf = (TableField*)lv_obj_get_user_data(table);
if (!tf) return;
uint16_t row = dsc->id / cols;
uint16_t col = dsc->id % cols;
tf->onDrawEnd(row, col, dsc);
}
TableField::TableField(Window* parent, const rect_t& rect, WindowFlags windowFlags) :
Window(parent, rect, windowFlags, 0, table_create)
{
lv_obj_add_event_cb(lvobj, TableField::event_cb, LV_EVENT_ALL, nullptr);
}
void TableField::setColumnCount(uint16_t cols)
{
lv_table_set_col_cnt(lvobj, cols);
}
uint16_t TableField::getColumnCount() const
{
return lv_table_get_col_cnt(lvobj);
}
void TableField::setRowCount(uint16_t rows)
{
lv_table_set_row_cnt(lvobj, rows);
}
uint16_t TableField::getRowCount() const
{
return lv_table_get_row_cnt(lvobj);
}
void TableField::setColumnWidth(uint16_t col, coord_t w)
{
lv_table_set_col_width(lvobj, col, w);
}
coord_t TableField::getColumnWidth(uint16_t col) const
{
return lv_table_get_col_width(lvobj, col);
}
void TableField::select(uint16_t row, uint16_t col)
{
lv_table_t* table = (lv_table_t*)lvobj;
if (table->row_act == row && table->col_act == row) return;
if (row >= table->row_cnt || col >= table->col_cnt) {
table->col_act = LV_TABLE_CELL_NONE;
table->row_act = LV_TABLE_CELL_NONE;
return;
}
table->row_act = row;
table->col_act = col;
lv_obj_invalidate(lvobj);
adjustScroll();
}
void TableField::adjustScroll()
{
lv_table_t* table = (lv_table_t*)lvobj;
// only vertical scroll for now
lv_coord_t h_before = 0;
for (uint16_t i = 0; i < table->row_act; i++) h_before += table->row_h[i];
lv_coord_t row_h = table->row_h[table->row_act];
lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj);
lv_obj_update_layout(lvobj);
lv_coord_t h = lv_obj_get_height(lvobj);
lv_coord_t diff_y = 0;
if (h_before < scroll_y) {
diff_y = scroll_y - h_before;
} else if (scroll_y + h < h_before + row_h) {
diff_y = scroll_y + h - h_before - row_h;
} else {
return;
}
lv_obj_scroll_by_bounded(lvobj, 0, diff_y, LV_ANIM_OFF);
}
void TableField::selectNext(int16_t dir)
{
lv_table_t* table = (lv_table_t*)lvobj;
if (table->col_act == LV_TABLE_CELL_NONE ||
table->row_act == LV_TABLE_CELL_NONE) {
if (table->col_cnt > 0 && table->row_cnt > 0) {
table->col_act = 0;
table->row_act = 0;
}
} else {
table->col_act += dir;
if (table->col_act >= table->col_cnt) {
table->col_act = 0;
table->row_act += dir;
if (table->row_act >= table->row_cnt) {
table->col_act = LV_TABLE_CELL_NONE;
table->row_act = LV_TABLE_CELL_NONE;
// wrap around
if (table->col_cnt > 0 && table->row_cnt > 0) {
if (dir < 0) {
table->col_act = table->col_cnt - 1;
table->row_act = table->row_cnt - 1;
} else {
table->col_act = 0;
table->row_act = 0;
}
}
}
}
}
lv_obj_invalidate(lvobj);
adjustScroll();
}
void TableField::onEvent(event_t event)
{
if (event == EVT_ROTARY_RIGHT) {
selectNext(1);
}
else if (event == EVT_ROTARY_LEFT) {
selectNext(-1);
}
else {
Window::onEvent(event);
}
}
+59
View File
@@ -0,0 +1,59 @@
/*
* 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.
*/
#pragma once
#include "form.h"
class TableField : public Window
{
public:
TableField(Window* parent, const rect_t& rect,
WindowFlags windowFlags = OPAQUE | FORM_NO_BORDER);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "Table"; }
#endif
void setColumnCount(uint16_t cols);
uint16_t getColumnCount() const;
void setRowCount(uint16_t rows);
uint16_t getRowCount() const;
void setColumnWidth(uint16_t col, coord_t w);
coord_t getColumnWidth(uint16_t col) const;
void select(uint16_t row, uint16_t col);
virtual void onPress(uint16_t row, uint16_t col) {}
protected:
void adjustScroll();
void selectNext(int16_t dir);
void onEvent(event_t event) override;
virtual void onSelected(uint16_t row, uint16_t col) {}
virtual void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {}
virtual void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {}
static void event_cb(lv_event_t *e);
static void clicked(lv_event_t *e);
static void draw_begin(lv_event_t *e);
static void draw_end(lv_event_t *e);
};
+113
View File
@@ -0,0 +1,113 @@
/*
* 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"
#include "widgets/field_edit.h"
#if !defined(STR_EDIT)
#define STR_EDIT "Edit"
#endif
#if !defined(STR_CLEAR)
#define STR_CLEAR "Clear"
#endif
#include "keyboard_text.h"
#if defined(HARDWARE_KEYS)
#include "menu.h"
#endif
#if defined(CLIPBOARD)
#include "clipboard.h"
#endif
TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value,
uint8_t length, LcdFlags windowFlags) :
FormField(parent, rect, windowFlags, 0,
field_edit_create),
value(value),
length(length)
{
// properties
lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF);
lv_textarea_set_password_mode(lvobj, false);
lv_textarea_set_one_line(lvobj, true);
lv_textarea_set_placeholder_text(lvobj, "---");
lv_textarea_set_max_length(lvobj, length);
setHeight(33);
padTop(5);
padLeft(4);
lv_obj_set_style_radius(lvobj, 4, 0);
if (width() == 0) {
lv_obj_set_width(lvobj, LV_DPI_DEF);
}
update();
}
void TextEdit::update()
{
// value may not be null-terminated
std::string txt(value, length);
lv_textarea_set_text(lvobj, txt.c_str());
}
void TextEdit::trim()
{
for (int i = length - 1; i >= 0; i--) {
if (value[i] == ' ' || value[i] == '\0')
value[i] = '\0';
else
break;
}
}
void TextEdit::changeEnd(bool forceChanged)
{
if (lvobj == nullptr) return;
bool changed = false;
auto text = lv_textarea_get_text(lvobj);
if (strncmp(value, text, length) != 0) {
changed = true;
}
if (changed || forceChanged) {
strncpy(value, text, length);
trim();
FormField::changeEnd();
}
}
void TextEdit::onClicked()
{
TextKeyboard::show(this);
}
void TextEdit::onFocusLost()
{
changeEnd();
FormField::onFocusLost();
}
+51
View File
@@ -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.
*/
#pragma once
#include "form.h"
class TextEdit : public FormField
{
public:
TextEdit(Window* parent, const rect_t& rect, char* value, uint8_t length,
LcdFlags windowFlags = 0);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "TextEdit"; }
#endif
uint8_t getMaxLength() const { return length; }
char* getData() const { return value; }
void update();
protected:
static void event_cb(lv_event_t* e);
char* value;
uint8_t length;
void trim();
void changeEnd(bool forceChanged = false) override;
void onClicked() override;
void onFocusLost() override;
};
+64
View File
@@ -0,0 +1,64 @@
/*
* 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.
*/
#pragma once
#include "bitmapbuffer.h"
#include "checkbox.h"
#include "button.h"
class Menu;
class MenuWindowContent;
class Dialog;
class DialogWindowContent;
class ChoiceBase;
enum IconState {
STATE_DEFAULT,
STATE_PRESSED,
};
class Theme {
public:
virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y,
coord_t w, coord_t h, int value, int total) const = 0;
virtual void drawCheckBox(BitmapBuffer* dc, CheckBox* checkBox) const
{
drawCheckBox(dc, checkBox->getValue(), 0, FIELD_PADDING_TOP,
checkBox->hasFocus());
const char* label = checkBox->getLabel();
if (label) dc->drawText(22, FIELD_PADDING_TOP, label);
}
virtual void drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus = false) const = 0;
virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0;
virtual const BitmapBuffer * getIconMask(uint8_t index) const = 0;
virtual TextButton * createTextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function<uint8_t(void)> pressHandler = nullptr, WindowFlags windowFlags = OPAQUE | BUTTON_BACKGROUND) const
{
return new TextButton(parent, rect, text, pressHandler, windowFlags);
}
};
extern Theme * theme;
MenuWindowContent * createMenuWindow(Menu * menu);
DialogWindowContent * createDialogWindow(Dialog * dialog, const rect_t & rect);
@@ -0,0 +1,52 @@
#include "edgetx_table.h"
#define MY_CLASS &table_class
static void table_event(const lv_obj_class_t * class_p, lv_event_t * e);
const lv_obj_class_t table_class = {
.constructor_cb = NULL,
.destructor_cb = NULL,
.event_cb = table_event,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT,
.base_class = &lv_table_class,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = 0,
};
lv_obj_t * table_create(lv_obj_t * parent)
{
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
static void table_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
lv_res_t res;
lv_event_code_t code = lv_event_get_code(e);
if (code != LV_EVENT_RELEASED) {
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
} else {
lv_obj_t * obj = lv_event_get_target(e);
lv_table_t * table = (lv_table_t *)obj;
/*From lv_table.c: handler for LV_EVENT_RELEASED*/
lv_obj_invalidate(obj);
lv_indev_t* indev = lv_indev_get_act();
lv_obj_t* scroll_obj = lv_indev_get_scroll_obj(indev);
if (table->col_act != LV_TABLE_CELL_NONE &&
table->row_act != LV_TABLE_CELL_NONE && scroll_obj == NULL) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if (res != LV_RES_OK) return;
}
}
}
@@ -0,0 +1,16 @@
#ifndef EDGETX_TABLE_H
#define EDGETX_TABLE_H
#include <lvgl/lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const lv_obj_class_t table_class;
lv_obj_t* table_create(lv_obj_t* parent);
#ifdef __cplusplus
};
#endif
#endif
+45
View File
@@ -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.
*/
#include "field_edit.h"
#define MY_CLASS &field_edit_class
static void field_edit_event(const lv_obj_class_t* class_p, lv_event_t* e)
{
/* Call the ancestor's event handler */
lv_obj_event_base(MY_CLASS, e);
}
const lv_obj_class_t field_edit_class = {
.base_class = &lv_textarea_class,
.event_cb = field_edit_event,
.width_def = LV_DPI_DEF / 2,
.height_def = LV_SIZE_CONTENT,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_textarea_t)
};
lv_obj_t* field_edit_create(lv_obj_t* parent)
{
lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
+36
View File
@@ -0,0 +1,36 @@
/*
* 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 FIELD_EDIT_H
#define FIELD_EDIT_H
#include <lvgl/lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const lv_obj_class_t field_edit_class;
lv_obj_t* field_edit_create(lv_obj_t* parent);
#ifdef __cplusplus
};
#endif
#endif
@@ -0,0 +1,41 @@
/*
* 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 "field_edit.h"
#define MY_CLASS &field_edit_class
const lv_obj_class_t field_edit_class = {
.base_class = &lv_obj_class,
.constructor_cb = lv_textarea_class.constructor_cb,
.destructor_cb = lv_textarea_class.destructor_cb,
.event_cb = lv_textarea_class.event_cb,
.width_def = LV_DPI_DEF / 2,
.height_def = LV_SIZE_CONTENT,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_textarea_t)
};
lv_obj_t* field_edit_create(lv_obj_t* parent)
{
lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
@@ -0,0 +1,36 @@
/*
* 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 FIELD_EDIT_H
#define FIELD_EDIT_H
#include <lvgl/lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const lv_obj_class_t field_edit_class;
lv_obj_t* field_edit_create(lv_obj_t* parent);
#ifdef __cplusplus
};
#endif
#endif
+38
View File
@@ -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.
*/
#include "simple_btn.h"
#define MY_CLASS &simple_btn_class
const lv_obj_class_t simple_btn_class = {
.width_def = LV_DPI_DEF,
.height_def = LV_DPI_DEF,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_btn_t),
.base_class = &lv_btn_class,
};
lv_obj_t* simple_btn_create(lv_obj_t* parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
+36
View File
@@ -0,0 +1,36 @@
/*
* 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 SIMPLE_BTN_H
#define SIMPLE_BTN_H
#include <lvgl/lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const lv_obj_class_t simple_btn_class;
lv_obj_t* simple_btn_create(lv_obj_t* parent);
#ifdef __cplusplus
};
#endif
#endif
@@ -0,0 +1,52 @@
/*
* 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_base.h"
void window_event_cb(lv_event_t* e);
#define MY_CLASS &window_base_class
static void window_base_event(const lv_obj_class_t* class_p, lv_event_t* e);
const lv_obj_class_t window_base_class = {
.base_class = &lv_obj_class,
.event_cb = window_base_event,
.width_def = LV_DPI_DEF,
.height_def = LV_DPI_DEF,
.editable = LV_OBJ_CLASS_EDITABLE_FALSE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
.instance_size = sizeof(lv_obj_t)
};
static void window_base_event(const lv_obj_class_t* class_p, lv_event_t* e)
{
/* Call the ancestor's event handler */
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if (res != LV_RES_OK) return;
window_event_cb(e);
}
lv_obj_t* window_create(lv_obj_t* parent)
{
lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
@@ -0,0 +1,36 @@
/*
* 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_BASE_H
#define WINDOW_BASE_H
#include <lvgl/lvgl.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const lv_obj_class_t window_base_class;
lv_obj_t* window_create(lv_obj_t* parent);
#ifdef __cplusplus
};
#endif
#endif
+639
View File
@@ -0,0 +1,639 @@
/*
* 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"
#include "touch.h"
#include "mainwindow.h"
#include "widgets/window_base.h"
std::list<Window *> Window::trash;
extern lv_obj_t *virtual_kb;
extern "C" void window_event_cb(lv_event_t * e)
{
lv_obj_t *target = lv_event_get_target(e);
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_SCROLL_BEGIN) {
TRACE("SCROLL_BEGIN");
} else if (code == LV_EVENT_SCROLL_END) {
TRACE("SCROLL_END");
}
Window* window = (Window *)lv_obj_get_user_data(target);
if (!window) return;
if(code == LV_EVENT_DELETE || window->deleted()) return;
if (code == LV_EVENT_DRAW_MAIN) {
lv_draw_ctx_t* draw_ctx = lv_event_get_draw_ctx(e);
lv_area_t a, clipping, obj_coords;
lv_area_copy(&a, draw_ctx->buf_area);
lv_area_copy(&clipping, draw_ctx->clip_area);
lv_obj_get_coords(target, &obj_coords);
auto w = a.x2 - a.x1 + 1;
auto h = a.y2 - a.y1 + 1;
TRACE_WINDOWS("DRAW_MAIN %s", window->getWindowDebugString().c_str());
BitmapBuffer buf = {BMP_RGB565, (uint16_t)w, (uint16_t)h,
(uint16_t *)draw_ctx->buf};
buf.setDrawCtx(draw_ctx);
buf.setOffset(obj_coords.x1 - a.x1, obj_coords.y1 - a.y1);
buf.setClippingRect(clipping.x1 - a.x1, clipping.x2 + 1 - a.x1,
clipping.y1 - a.y1, clipping.y2 + 1 - a.y1);
window->paint(&buf);
}
#if defined(HARDWARE_TOUCH)
else if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) {
TRACE_WINDOWS("PRESSED: %s", window->getWindowDebugString().c_str());
lv_indev_t *click_source = (lv_indev_t *)lv_event_get_param(e);
// if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD ||
// lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) {
// return;
// }
lv_area_t obj_coords;
lv_obj_get_coords(target, &obj_coords);
lv_point_t point_act;
lv_indev_get_point(click_source, &point_act);
// Ignore event from keypad
if (point_act.x < 0 || point_act.y < 0) return;
lv_point_t rel_pos;
rel_pos.x = point_act.x - obj_coords.x1;
rel_pos.y = point_act.y - obj_coords.y1;
if (code == LV_EVENT_PRESSED) {
TRACE("PRESSED[%d|%d]", rel_pos.x, rel_pos.y);
window->onTouchStart(rel_pos.x, rel_pos.y);
} else {
lv_point_t vect_act;
lv_indev_get_vect(click_source, &vect_act);
TRACE("RELEASED[%d|%d] vect[%d|%d]", rel_pos.x, rel_pos.y, vect_act.x,
vect_act.y, lv_indev_get_scroll_dir(click_source));
if (vect_act.x != 0 || vect_act.y != 0) return;
window->onTouchEnd(rel_pos.x, rel_pos.y);
}
}
#endif
else if (code == LV_EVENT_SCROLL) {
// exclude pointer based scrolling (only focus scrolling)
if (!lv_obj_is_scrolling(target)) {
lv_point_t* p = (lv_point_t*)lv_event_get_param(e);
lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(target);
lv_coord_t scroll_y = lv_obj_get_scroll_y(target);
TRACE("SCROLL[x=%d;y=%d;top=%d;bottom=%d]", p->x, p->y,
scroll_y, scroll_bottom);
if (scroll_y <= 45 && p->y > 0) {
lv_obj_scroll_by(target, 0, scroll_y, LV_ANIM_OFF);
} else if (scroll_bottom <= 16 && p->y < 0) {
lv_obj_scroll_by(target, 0, -scroll_bottom, LV_ANIM_OFF);
}
}
}
else if (code == LV_EVENT_CLICKED) {
TRACE("CLICKED[%p]", window);
window->onClicked();
}
else if (code == LV_EVENT_CANCEL) {
TRACE("CANCEL[%p]", window);
window->onCancel();
} else if (code == LV_EVENT_FOCUSED) {
if(window->focusHandler)
window->focusHandler(true);
} else if (code == LV_EVENT_DEFOCUSED) {
if(window->focusHandler)
window->focusHandler(false);
}
}
Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags,
LcdFlags textFlags, LvglCreate objConstruct) :
rect(rect),
parent(parent),
windowFlags(windowFlags),
textFlags(textFlags)
{
lv_obj_t* lv_parent = nullptr;
if (parent) lv_parent = parent->lvobj;
if (objConstruct != nullptr && objConstruct != window_create) {
lvobj = objConstruct(lv_parent);
lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, nullptr);
} else {
lvobj = window_create(lv_parent);
}
lv_obj_set_user_data(lvobj, this);
if (rect.w || rect.h) {
lv_obj_set_pos(lvobj, rect.x, rect.y);
lv_obj_set_size(lvobj, rect.w, rect.h);
}
if (windowFlags & OPAQUE) {
lv_obj_set_style_bg_opa(lvobj, LV_OPA_MAX, LV_PART_MAIN);
}
lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC);
if (parent) {
parent->addChild(this);
if (!(windowFlags & TRANSPARENT)) {
invalidate();
}
}
// honor the no focus flag of libopenui
if (this->windowFlags & NO_FOCUS) {
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
}
}
Window::Window(Window *parent, lv_obj_t* lvobj)
: rect(nullRect),
parent(parent),
lvobj(lvobj)
{
lv_obj_set_user_data(lvobj, this);
if (windowFlags & OPAQUE) {
lv_obj_set_style_bg_opa(lvobj, LV_OPA_MAX, LV_PART_MAIN);
}
lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC);
lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this);
if (parent) {
parent->addChild(this);
if (!(windowFlags & TRANSPARENT)) {
invalidate();
}
}
// honor the no focus flag of libopenui
if (this->windowFlags & NO_FOCUS) {
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
}
}
Window::~Window()
{
TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str());
if (children.size() > 0) deleteChildren();
if (lvobj != nullptr) {
lv_obj_set_user_data(lvobj, nullptr);
lv_obj_del(lvobj);
lvobj = nullptr;
}
}
#if defined(DEBUG_WINDOWS)
std::string Window::getName() const { return "Window"; }
std::string Window::getRectString() const
{
char result[32];
sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height());
return result;
}
std::string Window::getIndentString() const
{
std::string result;
auto tmp = parent;
while (tmp) {
result += " ";
tmp = tmp->getParent();
}
return result;
}
std::string Window::getWindowDebugString(const char *name) const
{
return getName() + (name ? std::string(" [") + name + "] " : " ") +
getRectString();
}
#endif
Window *Window::getFullScreenWindow()
{
if (width() == LCD_W && height() == LCD_H) return this;
if (parent) return parent->getFullScreenWindow();
return nullptr;
}
void Window::setWindowFlags(WindowFlags flags)
{
// TODO: check if LVGL flags need updating...
windowFlags = flags;
}
void Window::setTextFlags(LcdFlags flags)
{
textFlags = flags;
if (!lvobj) return;
// lv integration for colors
auto textColor = COLOR_VAL(flags);
auto r = GET_RED(textColor), g = GET_GREEN(textColor),
b = GET_BLUE(textColor);
lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN);
// // rco: shouldn't this be done via 'setTextFlags()' on the children?
// for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) {
// auto child = lv_obj_get_child(lvobj, i);
// lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN);
// }
}
void Window::attach(Window *newParent)
{
if (parent) detach();
parent = newParent;
if (newParent) {
newParent->addChild(this);
}
}
void Window::detach()
{
if (parent) {
parent->removeChild(this);
parent = nullptr;
}
}
void Window::deleteLater(bool detach, bool trash)
{
if (_deleted)
return;
TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str());
_deleted = true;
if (detach)
this->detach();
else
parent = nullptr;
if (trash) {
Window::trash.push_back(this);
}
if (closeHandler) {
closeHandler();
}
deleteChildren();
if (lvobj != nullptr) {
auto obj = lvobj;
lvobj = nullptr;
lv_obj_del(obj);
}
}
void Window::clear()
{
deleteChildren();
if(lvobj != nullptr) {
lv_obj_clean(lvobj);
}
invalidate();
}
void Window::deleteChildren()
{
// prevent LVGL refocus while mass-deleting
// inhibit_focus = true;
for (auto window: children) {
window->deleteLater(false);
}
// inhibit_focus = false;
children.clear();
}
bool Window::hasFocus() const
{
return lvobj && lv_obj_has_state(lvobj, LV_STATE_FOCUSED);
}
void Window::padLeft(coord_t pad)
{
lv_obj_set_style_pad_left(lvobj, pad, 0);
}
void Window::padRight(coord_t pad)
{
lv_obj_set_style_pad_right(lvobj, pad, 0);
}
void Window::padTop(coord_t pad)
{
lv_obj_set_style_pad_top(lvobj, pad, 0);
}
void Window::padBottom(coord_t pad)
{
lv_obj_set_style_pad_bottom(lvobj, pad, 0);
}
void Window::padAll(coord_t pad)
{
lv_obj_set_style_pad_all(lvobj, pad, 0);
}
void Window::padRow(coord_t pad)
{
lv_obj_set_style_pad_row(lvobj, pad, 0);
}
void Window::padColumn(coord_t pad)
{
lv_obj_set_style_pad_column(lvobj, pad, 0);
}
bool Window::hasOpaqueRect(const rect_t & testRect) const
{
if (!rect.contains(testRect))
return false;
if (windowFlags & OPAQUE) {
return true;
}
rect_t relativeRect = {testRect.x - rect.x, testRect.y - rect.y, testRect.w, testRect.h};
for (auto child: children) {
if (child->hasOpaqueRect(relativeRect))
return true;
}
return false;
}
void Window::fullPaint(BitmapBuffer * dc)
{
if(lvobj != nullptr)
lv_obj_invalidate(lvobj);
bool paintNeeded = true;
std::list<Window *>::iterator firstChild;
coord_t xmin, xmax, ymin, ymax;
dc->getClippingRect(xmin, xmax, ymin, ymax);
coord_t x = dc->getOffsetX();
coord_t y = dc->getOffsetY();
if (windowFlags & PAINT_CHILDREN_FIRST) {
paintChildren(dc, children.begin());
dc->setOffset(x, y);
dc->setClippingRect(xmin, xmax, ymin, ymax);
}
else {
firstChild = children.end();
rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin};
while (firstChild != children.begin()) {
auto child = *(--firstChild);
if (child->hasOpaqueRect(relativeRect)) {
paintNeeded = false;
break;
}
}
}
if (paintNeeded) {
TRACE_WINDOWS_INDENT("%s%s", getWindowDebugString().c_str(), hasFocus() ? " (*)" : "");
paint(dc);
#if defined(WINDOWS_INSPECT_BORDER_COLOR)
dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR);
#endif
}
else {
TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str());
}
if (!(windowFlags & PAINT_CHILDREN_FIRST)) {
paintChildren(dc, firstChild);
}
}
bool Window::isChildFullSize(const Window * child) const
{
return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width();
}
bool Window::isChildVisible(const Window * window) const
{
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, std::list<Window *>::iterator it)
{
coord_t x = dc->getOffsetX();
coord_t y = dc->getOffsetY();
coord_t xmin, xmax, ymin, ymax;
dc->getClippingRect(xmin, xmax, ymin, ymax);
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::bringToTop()
{
attach(parent); // does a detach + attach
if (lvobj && lv_obj_get_parent(lvobj)) lv_obj_move_foreground(lvobj);
}
void Window::checkEvents()
{
auto copy = children;
for (auto child: copy) {
if (!child->deleted()) {
child->checkEvents();
}
}
if (windowFlags & REFRESH_ALWAYS) {
invalidate();
}
}
void Window::onEvent(event_t event)
{
TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString("Window").c_str(), event);
if (parent) { parent->onEvent(event); }
}
void Window::onClicked()
{
if (parent && !(windowFlags & OPAQUE)) { parent->onClicked(); }
}
void Window::onCancel()
{
if (parent) { parent->onCancel(); }
}
#if defined(HARDWARE_TOUCH)
bool Window::onTouchStart(coord_t x, coord_t y)
{
if (parent && !(windowFlags & OPAQUE)) return parent->onTouchStart(x, y);
return true;
}
bool Window::onTouchEnd(coord_t x, coord_t y)
{
if (parent && !(windowFlags & OPAQUE)) return parent->onTouchEnd(x, y);
return true;
}
bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY)
{
// TODO: this is not used anymore: REMOVE
// for (auto it = children.rbegin(); it != children.rend(); ++it) {
// auto child = *it;
// if (child->rect.contains(startX, startY)) {
// if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) {
// return true;
// }
// }
// }
// return false;
return true;
}
#endif
coord_t Window::adjustHeight()
{
coord_t old = rect.h;
if (lvobj) {
lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
lv_obj_update_layout(lvobj);
rect.h = lv_obj_get_height(lvobj);
lv_obj_set_style_height(lvobj, rect.h, LV_PART_MAIN);
}
return rect.h - old;
}
void Window::moveWindowsTop(coord_t y, coord_t delta)
{
if (getWindowFlags() & FORWARD_SCROLL) {
parent->moveWindowsTop(bottom(), delta);
}
for (auto child: children) {
if (child->rect.y >= y) {
child->setTop(child->top() + delta);
invalidate();
}
}
}
void Window::updateSize()
{
lv_obj_set_height(lvobj, LV_SIZE_CONTENT);
lv_obj_update_layout(lvobj);
rect.x = lv_obj_get_x(lvobj);
rect.y = lv_obj_get_y(lvobj);
rect.w = lv_obj_get_width(lvobj);
rect.h = lv_obj_get_height(lvobj);
invalidate();
}
void Window::addChild(Window* window)
{
auto lv_parent = lv_obj_get_parent(window->lvobj);
if (lv_parent && (lv_parent != lvobj)) {
lv_obj_set_parent(window->lvobj, lvobj);
}
children.push_back(window);
}
void Window::removeChild(Window* window)
{
children.remove(window);
invalidate();
}
void Window::invalidate(const rect_t & rect)
{
if (lvobj) lv_obj_invalidate(lvobj);
}
+294
View File
@@ -0,0 +1,294 @@
/*
* 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.
*/
#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <list>
#include <string>
#include <utility>
#include <functional>
#include "bitmapbuffer.h"
#include "libopenui_defines.h"
#include "libopenui_helpers.h"
#include "libopenui_config.h"
#include "widgets/window_base.h"
#include "LvglWrapper.h"
typedef uint32_t WindowFlags;
#if !defined(_GNUC_)
#undef OPAQUE
#undef TRANSPARENT
#endif
constexpr int INFINITE_HEIGHT = INT32_MAX;
constexpr WindowFlags OPAQUE = 1u << 0u;
constexpr WindowFlags TRANSPARENT = 1u << 1u;
constexpr WindowFlags NO_SCROLLBAR = 1u << 2u;
constexpr WindowFlags NO_FOCUS = 1u << 3u;
constexpr WindowFlags FORWARD_SCROLL = 1u << 4u;
constexpr WindowFlags REFRESH_ALWAYS = 1u << 5u;
constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u;
constexpr WindowFlags PUSH_FRONT = 1u << 7u;
constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT;
// enum SetFocusFlag
// {
// SET_FOCUS_DEFAULT = 0,
// SET_FOCUS_FORWARD = 1,
// SET_FOCUS_BACKWARD = 2,
// SET_FOCUS_FIRST = 3,
// SET_FOCUS_NO_SCROLL= 4
// };
typedef lv_obj_t *(*LvglCreate)(lv_obj_t *);
extern "C" void window_event_cb(lv_event_t * e);
class Window
{
friend class GridLayout;
friend void window_event_cb(lv_event_t * e);
public:
Window(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0,
LcdFlags textFlags = 0, LvglCreate objConstruct = nullptr);
Window(Window *parent, lv_obj_t* lvobj);
virtual ~Window();
#if defined(DEBUG_WINDOWS)
virtual std::string getName() const;
std::string getRectString() const;
std::string getIndentString() const;
std::string getWindowDebugString(const char * name = nullptr) const;
#endif
Window *getParent() const { return parent; }
bool isChild(Window *window) const
{
return window == this || (parent && parent->isChild(window));
}
Window *getFullScreenWindow();
WindowFlags getWindowFlags() const { return windowFlags; }
void setWindowFlags(WindowFlags flags);
LcdFlags getTextFlags() const { return textFlags; }
void setTextFlags(LcdFlags flags);
typedef std::function<void()> CloseHandler;
void setCloseHandler(CloseHandler h) { closeHandler = std::move(h); }
typedef std::function<void(bool)> FocusHandler;
void setFocusHandler(FocusHandler h) { focusHandler = std::move(h); }
const std::list<Window *> getChildren() { return children; }
void clear();
virtual void deleteLater(bool detach = true, bool trash = true);
bool hasFocus() const;
void setRect(rect_t value)
{
rect = value;
lv_obj_enable_style_refresh(false);
lv_obj_set_pos(lvobj, rect.x, rect.y);
lv_obj_set_width(lvobj, rect.w);
lv_obj_enable_style_refresh(true);
lv_obj_set_height(lvobj, rect.h);
}
void setWidth(coord_t value)
{
rect.w = value;
lv_obj_set_width(lvobj, rect.w);
}
void setWindowCentered()
{
rect.x = (parent->width() - width()) / 2;
rect.y = (parent->height() - height()) / 2;
lv_obj_set_pos(lvobj, rect.x, rect.y);
}
void setHeight(coord_t value)
{
rect.h = value;
if (lvobj != nullptr) lv_obj_set_height(lvobj, rect.h);
}
void setLeft(coord_t x)
{
rect.x = x;
lv_obj_set_pos(lvobj, rect.x, rect.y);
}
void setTop(coord_t y)
{
rect.y = y;
lv_obj_set_pos(lvobj, rect.x, rect.y);
}
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 padLeft(coord_t pad);
void padRight(coord_t pad);
void padTop(coord_t pad);
void padBottom(coord_t pad);
void padAll(coord_t pad);
void padRow(coord_t pad);
void padColumn(coord_t pad);
bool isChildVisible(const Window * window) const;
bool isChildFullSize(const Window * window) const;
bool isVisible() const
{
return parent && parent->isChildVisible(this);
}
virtual void onEvent(event_t event);
virtual void onClicked();
virtual void onCancel();
coord_t adjustHeight();
void moveWindowsTop(coord_t y, coord_t delta);
virtual void updateSize();
void invalidate()
{
invalidate({0, 0, rect.w, rect.h});
}
void bringToTop();
virtual void checkEvents();
void attach(Window * window);
void detach();
bool deleted() const
{
return _deleted;
}
virtual void paint(BitmapBuffer *) {}
#if defined(HARDWARE_TOUCH)
virtual bool onTouchStart(coord_t x, coord_t y);
virtual bool onTouchEnd(coord_t x, coord_t y);
#endif
inline lv_obj_t *getLvObj() { return lvobj; }
protected:
static std::list<Window*> trash;
rect_t rect;
Window* parent = nullptr;
lv_obj_t* lvobj = nullptr;
std::list<Window *> children;
WindowFlags windowFlags = 0;
LcdFlags textFlags = 0;
bool _deleted = false;
std::function<void()> closeHandler;
std::function<void(bool)> focusHandler;
void deleteChildren();
virtual void addChild(Window * window);
void removeChild(Window * window);
virtual void invalidate(const rect_t & rect);
void fullPaint(BitmapBuffer * dc);
void paintChildren(BitmapBuffer * dc, std::list<Window *>::iterator it);
virtual void onFocusLost()
{
TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str());
if (focusHandler) {
focusHandler(false);
}
invalidate();
}
#if defined(HARDWARE_TOUCH)
virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX,
coord_t startY, coord_t slideX, coord_t slideY);
#endif
bool hasOpaqueRect(const rect_t & testRect) const;
};
+190
View File
@@ -0,0 +1,190 @@
set(LVGL_SOURCES_MINIMAL
core/lv_group.c
core/lv_obj.c
core/lv_event.c
core/lv_indev.c
core/lv_obj_scroll.c
core/lv_obj_draw.c
core/lv_disp.c
core/lv_obj_style.c
core/lv_theme.c
core/lv_obj_style_gen.c
core/lv_refr.c
core/lv_indev_scroll.c
core/lv_obj_class.c
core/lv_obj_tree.c
core/lv_obj_pos.c
draw/lv_draw_mask.c
draw/lv_img_cache.c
draw/sw/lv_draw_sw_gradient.c
draw/sw/lv_draw_sw.c
draw/sw/lv_draw_sw_blend.c
draw/sw/lv_draw_sw_letter.c
draw/sw/lv_draw_sw_arc.c
draw/sw/lv_draw_sw_polygon.c
draw/sw/lv_draw_sw_img.c
draw/sw/lv_draw_sw_line.c
draw/sw/lv_draw_sw_dither.c
draw/sw/lv_draw_sw_rect.c
draw/lv_draw_line.c
draw/lv_draw.c
draw/lv_img_buf.c
draw/lv_draw_rect.c
draw/lv_draw_label.c
draw/lv_draw_img.c
draw/lv_draw_arc.c
draw/lv_draw_triangle.c
draw/lv_img_decoder.c
# STM32 specific
draw/stm32_dma2d/lv_gpu_stm32_dma2d.c
# SDL specific
draw/sdl/lv_draw_sdl_rect.c
draw/sdl/lv_draw_sdl_arc.c
draw/sdl/lv_draw_sdl.c
draw/sdl/lv_draw_sdl_mask.c
draw/sdl/lv_draw_sdl_polygon.c
draw/sdl/lv_draw_sdl_stack_blur.c
draw/sdl/lv_draw_sdl_utils.c
draw/sdl/lv_draw_sdl_img.c
draw/sdl/lv_draw_sdl_bg.c
draw/sdl/lv_draw_sdl_label.c
draw/sdl/lv_draw_sdl_texture_cache.c
draw/sdl/lv_draw_sdl_line.c
draw/sdl/lv_draw_sdl_composite.c
hal/lv_hal_indev.c
hal/lv_hal_disp.c
hal/lv_hal_tick.c
font/lv_font.c
font/lv_font_loader.c
font/lv_font_fmt_txt.c
misc/lv_timer.c
misc/lv_txt.c
misc/lv_gc.c
misc/lv_style_gen.c
misc/lv_templ.c
misc/lv_color.c
misc/lv_fs.c
misc/lv_printf.c
misc/lv_style.c
misc/lv_log.c
misc/lv_area.c
misc/lv_math.c
misc/lv_lru.c
misc/lv_tlsf.c
misc/lv_anim_timeline.c
misc/lv_ll.c
misc/lv_bidi.c
misc/lv_mem.c
misc/lv_utils.c
misc/lv_async.c
misc/lv_anim.c
misc/lv_txt_ap.c
extra/lv_extra.c
)
set(LVGL_SOURCES
${LVGL_SOURCES_MINIMAL}
# font/lv_font_unscii_8.c
# font/lv_font_unscii_16.c
# font/lv_font_simsun_16_cjk.c
# font/lv_font_dejavu_16_persian_hebrew.c
# font/lv_font_montserrat_8.c
# font/lv_font_montserrat_10.c
# font/lv_font_montserrat_12.c
# font/lv_font_montserrat_12_subpx.c
# font/lv_font_montserrat_14.c
# font/lv_font_montserrat_16.c
# font/lv_font_montserrat_18.c
# font/lv_font_montserrat_20.c
# font/lv_font_montserrat_22.c
# font/lv_font_montserrat_24.c
# font/lv_font_montserrat_26.c
# font/lv_font_montserrat_28.c
# font/lv_font_montserrat_28_compressed.c
# font/lv_font_montserrat_30.c
# font/lv_font_montserrat_32.c
# font/lv_font_montserrat_34.c
# font/lv_font_montserrat_36.c
# font/lv_font_montserrat_38.c
# font/lv_font_montserrat_40.c
# font/lv_font_montserrat_42.c
# font/lv_font_montserrat_44.c
# font/lv_font_montserrat_46.c
# font/lv_font_montserrat_48.c
widgets/lv_line.c
widgets/lv_dropdown.c
widgets/lv_objx_templ.c
widgets/lv_canvas.c
widgets/lv_switch.c
widgets/lv_btnmatrix.c
widgets/lv_bar.c
widgets/lv_roller.c
widgets/lv_img.c
widgets/lv_btn.c
widgets/lv_checkbox.c
widgets/lv_label.c
widgets/lv_table.c
widgets/lv_slider.c
widgets/lv_textarea.c
widgets/lv_arc.c
extra/others/monkey/lv_monkey.c
extra/others/gridnav/lv_gridnav.c
extra/others/snapshot/lv_snapshot.c
extra/layouts/grid/lv_grid.c
extra/layouts/flex/lv_flex.c
extra/libs/bmp/lv_bmp.c
extra/libs/ffmpeg/lv_ffmpeg.c
extra/libs/gif/lv_gif.c
extra/libs/gif/gifdec.c
extra/libs/freetype/lv_freetype.c
extra/libs/qrcode/lv_qrcode.c
extra/libs/qrcode/qrcodegen.c
extra/libs/sjpg/tjpgd.c
extra/libs/sjpg/lv_sjpg.c
extra/libs/fsdrv/lv_fs_win32.c
extra/libs/fsdrv/lv_fs_stdio.c
extra/libs/fsdrv/lv_fs_posix.c
extra/libs/fsdrv/lv_fs_fatfs.c
extra/libs/rlottie/lv_rlottie.c
extra/libs/png/lv_png.c
extra/libs/png/lodepng.c
extra/widgets/imgbtn/lv_imgbtn.c
extra/widgets/menu/lv_menu.c
extra/widgets/win/lv_win.c
extra/widgets/colorwheel/lv_colorwheel.c
extra/widgets/list/lv_list.c
extra/widgets/chart/lv_chart.c
extra/widgets/calendar/lv_calendar_header_arrow.c
extra/widgets/calendar/lv_calendar.c
extra/widgets/calendar/lv_calendar_header_dropdown.c
extra/widgets/tileview/lv_tileview.c
extra/widgets/led/lv_led.c
extra/widgets/spinner/lv_spinner.c
extra/widgets/tabview/lv_tabview.c
extra/widgets/meter/lv_meter.c
extra/widgets/spinbox/lv_spinbox.c
extra/widgets/animimg/lv_animimg.c
extra/widgets/span/lv_span.c
extra/widgets/keyboard/lv_keyboard.c
extra/widgets/msgbox/lv_msgbox.c
# extra/themes/basic/lv_theme_basic.c
# extra/themes/default/lv_theme_default.c
# extra/themes/mono/lv_theme_mono.c
)
foreach(LVGL_FILE ${LVGL_SOURCES_MINIMAL})
set(LVGL_SRC_FILES_MINIMAL ${LVGL_SRC_FILES_MINIMAL} ${LVGL_SRC_DIR}/${LVGL_FILE})
endforeach()
foreach(LVGL_FILE ${LVGL_SOURCES})
set(LVGL_SRC_FILES ${LVGL_SRC_FILES} ${LVGL_SRC_DIR}/${LVGL_FILE})
endforeach()
set(SRC ${SRC} ${LVGL_SRC_FILES})
Submodule radio/src/thirdparty/libopenui/thirdparty/lvgl added at 9a414b1d48
+24
View File
@@ -0,0 +1,24 @@
LZ4 Library
Copyright (c) 2011-2016, Yann Collet
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+137
View File
@@ -0,0 +1,137 @@
LZ4 - Library Files
================================
The `/lib` directory contains many files, but depending on project's objectives,
not all of them are necessary.
#### Minimal LZ4 build
The minimum required is **`lz4.c`** and **`lz4.h`**,
which provides the fast compression and decompression algorithms.
They generate and decode data using the [LZ4 block format].
#### High Compression variant
For more compression ratio at the cost of compression speed,
the High Compression variant called **lz4hc** is available.
Add files **`lz4hc.c`** and **`lz4hc.h`**.
This variant also compresses data using the [LZ4 block format],
and depends on regular `lib/lz4.*` source files.
#### Frame support, for interoperability
In order to produce compressed data compatible with `lz4` command line utility,
it's necessary to use the [official interoperable frame format].
This format is generated and decoded automatically by the **lz4frame** library.
Its public API is described in `lib/lz4frame.h`.
In order to work properly, lz4frame needs all other modules present in `/lib`,
including, lz4 and lz4hc, and also **xxhash**.
So it's necessary to include all `*.c` and `*.h` files present in `/lib`.
#### Advanced / Experimental API
Definitions which are not guaranteed to remain stable in future versions,
are protected behind macros, such as `LZ4_STATIC_LINKING_ONLY`.
As the name strongly implies, these definitions should only be invoked
in the context of static linking ***only***.
Otherwise, dependent application may fail on API or ABI break in the future.
The associated symbols are also not exposed by the dynamic library by default.
Should they be nonetheless needed, it's possible to force their publication
by using build macros `LZ4_PUBLISH_STATIC_FUNCTIONS`
and `LZ4F_PUBLISH_STATIC_FUNCTIONS`.
#### Build macros
The following build macro can be selected to adjust source code behavior at compilation time :
- `LZ4_FAST_DEC_LOOP` : this triggers a speed optimized decompression loop, more powerful on modern cpus.
This loop works great on `x86`, `x64` and `aarch64` cpus, and is automatically enabled for them.
It's also possible to enable or disable it manually, by passing `LZ4_FAST_DEC_LOOP=1` or `0` to the preprocessor.
For example, with `gcc` : `-DLZ4_FAST_DEC_LOOP=1`,
and with `make` : `CPPFLAGS+=-DLZ4_FAST_DEC_LOOP=1 make lz4`.
- `LZ4_DISTANCE_MAX` : control the maximum offset that the compressor will allow.
Set to 65535 by default, which is the maximum value supported by lz4 format.
Reducing maximum distance will reduce opportunities for LZ4 to find matches,
hence will produce a worse compression ratio.
However, a smaller max distance can allow compatibility with specific decoders using limited memory budget.
This build macro only influences the compressed output of the compressor.
- `LZ4_DISABLE_DEPRECATE_WARNINGS` : invoking a deprecated function will make the compiler generate a warning.
This is meant to invite users to update their source code.
Should this be a problem, it's generally possible to make the compiler ignore these warnings,
for example with `-Wno-deprecated-declarations` on `gcc`,
or `_CRT_SECURE_NO_WARNINGS` for Visual Studio.
This build macro offers another project-specific method
by defining `LZ4_DISABLE_DEPRECATE_WARNINGS` before including the LZ4 header files.
- `LZ4_USER_MEMORY_FUNCTIONS` : replace calls to <stdlib>'s `malloc`, `calloc` and `free`
by user-defined functions, which must be called `LZ4_malloc()`, `LZ4_calloc()` and `LZ4_free()`.
User functions must be available at link time.
- `LZ4_FORCE_SW_BITCOUNT` : by default, the compression algorithm tries to determine lengths
by using bitcount instructions, generally implemented as fast single instructions in many cpus.
In case the target cpus doesn't support it, or compiler intrinsic doesn't work, or feature bad performance,
it's possible to use an optimized software path instead.
This is achieved by setting this build macros .
In most cases, it's not expected to be necessary,
but it can be legitimately considered for less common platforms.
- `LZ4_ALIGN_TEST` : alignment test ensures that the memory area
passed as argument to become a compression state is suitably aligned.
This test can be disabled if it proves flaky, by setting this value to 0.
#### Amalgamation
lz4 source code can be amalgamated into a single file.
One can combine all source code into `lz4_all.c` by using following command:
```
cat lz4.c lz4hc.c lz4frame.c > lz4_all.c
```
(`cat` file order is important) then compile `lz4_all.c`.
All `*.h` files present in `/lib` remain necessary to compile `lz4_all.c`.
#### Windows : using MinGW+MSYS to create DLL
DLL can be created using MinGW+MSYS with the `make liblz4` command.
This command creates `dll\liblz4.dll` and the import library `dll\liblz4.lib`.
To override the `dlltool` command when cross-compiling on Linux, just set the `DLLTOOL` variable. Example of cross compilation on Linux with mingw-w64 64 bits:
```
make BUILD_STATIC=no CC=x86_64-w64-mingw32-gcc DLLTOOL=x86_64-w64-mingw32-dlltool OS=Windows_NT
```
The import library is only required with Visual C++.
The header files `lz4.h`, `lz4hc.h`, `lz4frame.h` and the dynamic library
`dll\liblz4.dll` are required to compile a project using gcc/MinGW.
The dynamic library has to be added to linking options.
It means that if a project that uses LZ4 consists of a single `test-dll.c`
file it should be linked with `dll\liblz4.dll`. For example:
```
$(CC) $(CFLAGS) -Iinclude/ test-dll.c -o test-dll dll\liblz4.dll
```
The compiled executable will require LZ4 DLL which is available at `dll\liblz4.dll`.
#### Miscellaneous
Other files present in the directory are not source code. They are :
- `LICENSE` : contains the BSD license text
- `Makefile` : `make` script to compile and install lz4 library (static and dynamic)
- `liblz4.pc.in` : for `pkg-config` (used in `make install`)
- `README.md` : this file
[official interoperable frame format]: ../doc/lz4_Frame_format.md
[LZ4 block format]: ../doc/lz4_Block_format.md
#### License
All source material within __lib__ directory are BSD 2-Clause licensed.
See [LICENSE](LICENSE) for details.
The license is also reminded at the top of each source file.
File diff suppressed because it is too large Load Diff
+774
View File
@@ -0,0 +1,774 @@
/*
* LZ4 - Fast LZ compression algorithm
* Header File
* Copyright (C) 2011-present, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- LZ4 homepage : http://www.lz4.org
- LZ4 source repository : https://github.com/lz4/lz4
*/
#if defined (__cplusplus)
extern "C" {
#endif
#ifndef LZ4_H_2983827168210
#define LZ4_H_2983827168210
/* --- Dependency --- */
#include <stddef.h> /* size_t */
/**
Introduction
LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
The LZ4 compression library provides in-memory compression and decompression functions.
It gives full buffer control to user.
Compression can be done in:
- a single step (described as Simple Functions)
- a single step, reusing a context (described in Advanced Functions)
- unbounded multiple steps (described as Streaming compression)
lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
Decompressing such a compressed block requires additional metadata.
Exact metadata depends on exact decompression function.
For the typical case of LZ4_decompress_safe(),
metadata includes block's compressed size, and maximum bound of decompressed size.
Each application is free to encode and pass such metadata in whichever way it wants.
lz4.h only handle blocks, it can not generate Frames.
Blocks are different from Frames (doc/lz4_Frame_format.md).
Frames bundle both blocks and metadata in a specified manner.
Embedding metadata is required for compressed data to be self-contained and portable.
Frame format is delivered through a companion API, declared in lz4frame.h.
The `lz4` CLI can only manage frames.
*/
/*^***************************************************************
* Export parameters
*****************************************************************/
/*
* LZ4_DLL_EXPORT :
* Enable exporting of functions when building a Windows DLL
* LZ4LIB_VISIBILITY :
* Control library symbols visibility.
*/
#ifndef LZ4LIB_VISIBILITY
# if defined(__GNUC__) && (__GNUC__ >= 4)
# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
# else
# define LZ4LIB_VISIBILITY
# endif
#endif
#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
# define LZ4LIB_API LZ4LIB_VISIBILITY
#endif
/*------ Version ------*/
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 3 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
#define LZ4_QUOTE(str) #str
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */
/*-************************************
* Tuning parameter
**************************************/
/*!
* LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
* Increasing memory usage improves compression ratio.
* Reduced memory usage may improve speed, thanks to better cache locality.
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
*/
#ifndef LZ4_MEMORY_USAGE
# define LZ4_MEMORY_USAGE 14
#endif
/*-************************************
* Simple Functions
**************************************/
/*! LZ4_compress_default() :
* Compresses 'srcSize' bytes from buffer 'src'
* into already allocated 'dst' buffer of size 'dstCapacity'.
* Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
* It also runs faster, so it's a recommended setting.
* If the function cannot compress 'src' into a more limited 'dst' budget,
* compression stops *immediately*, and the function result is zero.
* In which case, 'dst' content is undefined (invalid).
* srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
* dstCapacity : size of buffer 'dst' (which must be already allocated)
* @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
* or 0 if compression fails
* Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
*/
LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_safe() :
* compressedSize : is the exact complete size of the compressed block.
* dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size.
* @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
* Note 1 : This function is protected against malicious data packets :
* it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
* even if the compressed block is maliciously modified to order the decoder to do these actions.
* In such case, the decoder stops immediately, and considers the compressed block malformed.
* Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
* The implementation is free to send / store / derive this information in whichever way is most beneficial.
* If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
*/
LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
/*-************************************
* Advanced Functions
**************************************/
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
/*! LZ4_compressBound() :
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
This function is primarily useful for memory allocation purposes (destination buffer size).
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
return : maximum output size in a "worst case" scenario
or 0, if input size is incorrect (too large or negative)
*/
LZ4LIB_API int LZ4_compressBound(int inputSize);
/*! LZ4_compress_fast() :
Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
An acceleration value of "1" is the same as regular LZ4_compress_default()
Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).
Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).
*/
LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_fast_extState() :
* Same as LZ4_compress_fast(), using an externally allocated memory space for its state.
* Use LZ4_sizeofState() to know how much memory must be allocated,
* and allocate it on 8-bytes boundaries (using `malloc()` typically).
* Then, provide this buffer as `void* state` to compression function.
*/
LZ4LIB_API int LZ4_sizeofState(void);
LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_compress_destSize() :
* Reverse the logic : compresses as much data as possible from 'src' buffer
* into already allocated buffer 'dst', of size >= 'targetDestSize'.
* This function either compresses the entire 'src' content into 'dst' if it's large enough,
* or fill 'dst' buffer completely with as much data as possible from 'src'.
* note: acceleration parameter is fixed to "default".
*
* *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
* New value is necessarily <= input value.
* @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
* or 0 if compression fails.
*
* Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):
* the produced compressed content could, in specific circumstances,
* require to be decompressed into a destination buffer larger
* by at least 1 byte than the content to decompress.
* If an application uses `LZ4_compress_destSize()`,
* it's highly recommended to update liblz4 to v1.9.2 or better.
* If this can't be done or ensured,
* the receiving decompression function should provide
* a dstCapacity which is > decompressedSize, by at least 1 byte.
* See https://github.com/lz4/lz4/issues/859 for details
*/
LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
/*! LZ4_decompress_safe_partial() :
* Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
* into destination buffer 'dst' of size 'dstCapacity'.
* Up to 'targetOutputSize' bytes will be decoded.
* The function stops decoding on reaching this objective.
* This can be useful to boost performance
* whenever only the beginning of a block is required.
*
* @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)
* If source stream is detected malformed, function returns a negative result.
*
* Note 1 : @return can be < targetOutputSize, if compressed block contains less data.
*
* Note 2 : targetOutputSize must be <= dstCapacity
*
* Note 3 : this function effectively stops decoding on reaching targetOutputSize,
* so dstCapacity is kind of redundant.
* This is because in older versions of this function,
* decoding operation would still write complete sequences.
* Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,
* it could write more bytes, though only up to dstCapacity.
* Some "margin" used to be required for this operation to work properly.
* Thankfully, this is no longer necessary.
* The function nonetheless keeps the same signature, in an effort to preserve API compatibility.
*
* Note 4 : If srcSize is the exact size of the block,
* then targetOutputSize can be any value,
* including larger than the block's decompressed size.
* The function will, at most, generate block's decompressed size.
*
* Note 5 : If srcSize is _larger_ than block's compressed size,
* then targetOutputSize **MUST** be <= block's decompressed size.
* Otherwise, *silent corruption will occur*.
*/
LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
/*-*********************************************
* Streaming Compression Functions
***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
/*! LZ4_resetStream_fast() : v1.9.0+
* Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
* (e.g., LZ4_compress_fast_continue()).
*
* An LZ4_stream_t must be initialized once before usage.
* This is automatically done when created by LZ4_createStream().
* However, should the LZ4_stream_t be simply declared on stack (for example),
* it's necessary to initialize it first, using LZ4_initStream().
*
* After init, start any new stream with LZ4_resetStream_fast().
* A same LZ4_stream_t can be re-used multiple times consecutively
* and compress multiple streams,
* provided that it starts each new stream with LZ4_resetStream_fast().
*
* LZ4_resetStream_fast() is much faster than LZ4_initStream(),
* but is not compatible with memory regions containing garbage data.
*
* Note: it's only useful to call LZ4_resetStream_fast()
* in the context of streaming compression.
* The *extState* functions perform their own resets.
* Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.
*/
LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
/*! LZ4_loadDict() :
* Use this function to reference a static dictionary into LZ4_stream_t.
* The dictionary must remain available during compression.
* LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
* The same dictionary will have to be loaded on decompression side for successful decoding.
* Dictionary are useful for better compression of small data (KB range).
* While LZ4 accept any input as dictionary,
* results are generally better when using Zstandard's Dictionary Builder.
* Loading a size of 0 is allowed, and is the same as reset.
* @return : loaded dictionary size, in bytes (necessarily <= 64 KB)
*/
LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
/*! LZ4_compress_fast_continue() :
* Compress 'src' content using data from previously compressed blocks, for better compression ratio.
* 'dst' buffer must be already allocated.
* If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
*
* @return : size of compressed block
* or 0 if there is an error (typically, cannot fit into 'dst').
*
* Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
* Each block has precise boundaries.
* Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.
* It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
*
* Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !
*
* Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.
* Make sure that buffers are separated, by at least one byte.
* This construction ensures that each block only depends on previous block.
*
* Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
*
* Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.
*/
LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_saveDict() :
* If last 64KB data cannot be guaranteed to remain available at its current memory location,
* save it into a safer place (char* safeBuffer).
* This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
* but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
* @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
*/
LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
/*-**********************************************
* Streaming Decompression Functions
* Bufferless synchronous API
************************************************/
typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
* creation / destruction of streaming decompression tracking context.
* A tracking context can be re-used multiple times.
*/
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
/*! LZ4_setStreamDecode() :
* An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
* Use this function to start decompression of a new stream of blocks.
* A dictionary can optionally be set. Use NULL or size 0 for a reset order.
* Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.
* @return : 1 if OK, 0 if error
*/
LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
/*! LZ4_decoderRingBufferSize() : v1.8.2+
* Note : in a ring buffer scenario (optional),
* blocks are presumed decompressed next to each other
* up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),
* at which stage it resumes from beginning of ring buffer.
* When setting such a ring buffer for streaming decompression,
* provides the minimum size of this ring buffer
* to be compatible with any source respecting maxBlockSize condition.
* @return : minimum ring buffer size,
* or 0 if there is an error (invalid maxBlockSize).
*/
LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */
/*! LZ4_decompress_*_continue() :
* These decoding functions allow decompression of consecutive blocks in "streaming" mode.
* A block is an unsplittable entity, it must be presented entirely to a decompression function.
* Decompression functions only accepts one block at a time.
* The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.
* If less than 64KB of data has been decoded, all the data must be present.
*
* Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
* - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).
* maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.
* In which case, encoding and decoding buffers do not need to be synchronized.
* Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.
* - Synchronized mode :
* Decompression buffer size is _exactly_ the same as compression buffer size,
* and follows exactly same update rule (block boundaries at same positions),
* and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),
* _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).
* - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.
* In which case, encoding and decoding buffers do not need to be synchronized,
* and encoding ring buffer can have any size, including small ones ( < 64 KB).
*
* Whenever these conditions are not possible,
* save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
* then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
*/
LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_*_usingDict() :
* These decoding functions work the same as
* a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
* They are stand-alone, and don't need an LZ4_streamDecode_t structure.
* Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize);
#endif /* LZ4_H_2983827168210 */
/*^*************************************
* !!!!!! STATIC LINKING ONLY !!!!!!
***************************************/
/*-****************************************************************************
* Experimental section
*
* Symbols declared in this section must be considered unstable. Their
* signatures or semantics may change, or they may be removed altogether in the
* future. They are therefore only safe to depend on when the caller is
* statically linked against the library.
*
* To protect against unsafe usage, not only are the declarations guarded,
* the definitions are hidden by default
* when building LZ4 as a shared/dynamic library.
*
* In order to access these declarations,
* define LZ4_STATIC_LINKING_ONLY in your application
* before including LZ4's headers.
*
* In order to make their implementations accessible dynamically, you must
* define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
******************************************************************************/
#ifdef LZ4_STATIC_LINKING_ONLY
#ifndef LZ4_STATIC_3504398509
#define LZ4_STATIC_3504398509
#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
#define LZ4LIB_STATIC_API LZ4LIB_API
#else
#define LZ4LIB_STATIC_API
#endif
/*! LZ4_compress_fast_extState_fastReset() :
* A variant of LZ4_compress_fast_extState().
*
* Using this variant avoids an expensive initialization step.
* It is only safe to call if the state buffer is known to be correctly initialized already
* (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized").
* From a high level, the difference is that
* this function initializes the provided state with a call to something like LZ4_resetStream_fast()
* while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
*/
LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
/*! LZ4_attach_dictionary() :
* This is an experimental API that allows
* efficient use of a static dictionary many times.
*
* Rather than re-loading the dictionary buffer into a working context before
* each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
* working LZ4_stream_t, this function introduces a no-copy setup mechanism,
* in which the working stream references the dictionary stream in-place.
*
* Several assumptions are made about the state of the dictionary stream.
* Currently, only streams which have been prepared by LZ4_loadDict() should
* be expected to work.
*
* Alternatively, the provided dictionaryStream may be NULL,
* in which case any existing dictionary stream is unset.
*
* If a dictionary is provided, it replaces any pre-existing stream history.
* The dictionary contents are the only history that can be referenced and
* logically immediately precede the data compressed in the first subsequent
* compression call.
*
* The dictionary will only remain attached to the working stream through the
* first compression call, at the end of which it is cleared. The dictionary
* stream (and source buffer) must remain in-place / accessible / unchanged
* through the completion of the first compression call on the stream.
*/
LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream);
/*! In-place compression and decompression
*
* It's possible to have input and output sharing the same buffer,
* for highly contrained memory environments.
* In both cases, it requires input to lay at the end of the buffer,
* and decompression to start at beginning of the buffer.
* Buffer size must feature some margin, hence be larger than final size.
*
* |<------------------------buffer--------------------------------->|
* |<-----------compressed data--------->|
* |<-----------decompressed size------------------>|
* |<----margin---->|
*
* This technique is more useful for decompression,
* since decompressed size is typically larger,
* and margin is short.
*
* In-place decompression will work inside any buffer
* which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
* This presumes that decompressedSize > compressedSize.
* Otherwise, it means compression actually expanded data,
* and it would be more efficient to store such data with a flag indicating it's not compressed.
* This can happen when data is not compressible (already compressed, or encrypted).
*
* For in-place compression, margin is larger, as it must be able to cope with both
* history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
* and data expansion, which can happen when input is not compressible.
* As a consequence, buffer size requirements are much higher,
* and memory savings offered by in-place compression are more limited.
*
* There are ways to limit this cost for compression :
* - Reduce history size, by modifying LZ4_DISTANCE_MAX.
* Note that it is a compile-time constant, so all compressions will apply this limit.
* Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
* so it's a reasonable trick when inputs are known to be small.
* - Require the compressor to deliver a "maximum compressed size".
* This is the `dstCapacity` parameter in `LZ4_compress*()`.
* When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
* in which case, the return code will be 0 (zero).
* The caller must be ready for these cases to happen,
* and typically design a backup scheme to send data uncompressed.
* The combination of both techniques can significantly reduce
* the amount of margin required for in-place compression.
*
* In-place compression can work in any buffer
* which size is >= (maxCompressedSize)
* with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
* LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
* so it's possible to reduce memory requirements by playing with them.
*/
#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32)
#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */
# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
#endif
#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
#endif /* LZ4_STATIC_3504398509 */
#endif /* LZ4_STATIC_LINKING_ONLY */
#ifndef LZ4_H_98237428734687
#define LZ4_H_98237428734687
/*-************************************************************
* Private Definitions
**************************************************************
* Do not use these definitions directly.
* They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
* Accessing members will expose user code to API and/or ABI break in future versions of the library.
**************************************************************/
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
# include <stdint.h>
typedef int8_t LZ4_i8;
typedef uint8_t LZ4_byte;
typedef uint16_t LZ4_u16;
typedef uint32_t LZ4_u32;
#else
typedef signed char LZ4_i8;
typedef unsigned char LZ4_byte;
typedef unsigned short LZ4_u16;
typedef unsigned int LZ4_u32;
#endif
typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
struct LZ4_stream_t_internal {
LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];
LZ4_u32 currentOffset;
LZ4_u32 tableType;
const LZ4_byte* dictionary;
const LZ4_stream_t_internal* dictCtx;
LZ4_u32 dictSize;
};
typedef struct {
const LZ4_byte* externalDict;
size_t extDictSize;
const LZ4_byte* prefixEnd;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
/*! LZ4_stream_t :
* Do not use below internal definitions directly !
* Declare or allocate an LZ4_stream_t instead.
* LZ4_stream_t can also be created using LZ4_createStream(), which is recommended.
* The structure definition can be convenient for static allocation
* (on stack, or as part of larger structure).
* Init this structure with LZ4_initStream() before first use.
* note : only use this definition in association with static linking !
* this definition is not API/ABI safe, and may change in future versions.
*/
#define LZ4_STREAMSIZE 16416 /* static size, for inter-version compatibility */
#define LZ4_STREAMSIZE_VOIDP (LZ4_STREAMSIZE / sizeof(void*))
union LZ4_stream_u {
void* table[LZ4_STREAMSIZE_VOIDP];
LZ4_stream_t_internal internal_donotuse;
}; /* previously typedef'd to LZ4_stream_t */
/*! LZ4_initStream() : v1.9.0+
* An LZ4_stream_t structure must be initialized at least once.
* This is automatically done when invoking LZ4_createStream(),
* but it's not when the structure is simply declared on stack (for example).
*
* Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.
* It can also initialize any arbitrary buffer of sufficient size,
* and will @return a pointer of proper type upon initialization.
*
* Note : initialization fails if size and alignment conditions are not respected.
* In which case, the function will @return NULL.
* Note2: An LZ4_stream_t structure guarantees correct alignment and size.
* Note3: Before v1.9.0, use LZ4_resetStream() instead
*/
LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);
/*! LZ4_streamDecode_t :
* information structure to track an LZ4 stream during decompression.
* init this structure using LZ4_setStreamDecode() before first use.
* note : only use in association with static linking !
* this definition is not API/ABI safe,
* and may change in a future version !
*/
#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ )
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
union LZ4_streamDecode_u {
unsigned long long table[LZ4_STREAMDECODESIZE_U64];
LZ4_streamDecode_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_streamDecode_t */
/*-************************************
* Obsolete Functions
**************************************/
/*! Deprecation warnings
*
* Deprecated functions make the compiler generate a warning when invoked.
* This is meant to invite users to update their source code.
* Should deprecation warnings be a problem, it is generally possible to disable them,
* typically with -Wno-deprecated-declarations for gcc
* or _CRT_SECURE_NO_WARNINGS in Visual.
*
* Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS
* before including the header file.
*/
#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
#else
# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
# define LZ4_DEPRECATED(message) [[deprecated(message)]]
# elif defined(_MSC_VER)
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
# else
# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")
# define LZ4_DEPRECATED(message) /* disabled */
# endif
#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
/*! Obsolete compression functions (since v1.7.3) */
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize);
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
/*! Obsolete decompression functions (since v1.8.0) */
LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
/* Obsolete streaming functions (since v1.7.0)
* degraded functionality; do not use!
*
* In order to perform streaming compression, these functions depended on data
* that is no longer tracked in the state. They have been preserved as well as
* possible: using them will still produce a correct output. However, they don't
* actually retain any history between compression calls. The compression ratio
* achieved will therefore be no better than compressing each chunk
* independently.
*/
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
/*! Obsolete streaming decoding functions (since v1.7.0) */
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :
* These functions used to be faster than LZ4_decompress_safe(),
* but this is no longer the case. They are now slower.
* This is because LZ4_decompress_fast() doesn't know the input size,
* and therefore must progress more cautiously into the input buffer to not read beyond the end of block.
* On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
* As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
*
* The last remaining LZ4_decompress_fast() specificity is that
* it can decompress a block without knowing its compressed size.
* Such functionality can be achieved in a more secure manner
* by employing LZ4_decompress_safe_partial().
*
* Parameters:
* originalSize : is the uncompressed size to regenerate.
* `dst` must be already allocated, its size must be >= 'originalSize' bytes.
* @return : number of bytes read from source buffer (== compressed size).
* The function expects to finish at block's end exactly.
* If the source stream is detected malformed, the function stops decoding and returns a negative result.
* note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.
* However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.
* Also, since match offsets are not validated, match reads from 'src' may underflow too.
* These issues never happen if input (compressed) data is correct.
* But they may happen if input data is invalid (error or intentional tampering).
* As a consequence, use these functions in trusted environments with trusted data **only**.
*/
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead")
LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead")
LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead")
LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
/*! LZ4_resetStream() :
* An LZ4_stream_t structure must be initialized at least once.
* This is done with LZ4_initStream(), or LZ4_resetStream().
* Consider switching to LZ4_initStream(),
* invoking LZ4_resetStream() will trigger deprecation warnings in the future.
*/
LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
#endif /* LZ4_H_98237428734687 */
#if defined (__cplusplus)
}
#endif
Submodule radio/src/thirdparty/libopenui/thirdparty/stb added at 7cce4c3ad9
+213
View File
@@ -0,0 +1,213 @@
#!/usr/bin/env python
import argparse
from PIL import Image
# LZ4 compression
import lz4.block
class RawMixin:
def encode_byte(self, byte):
self.write(byte)
def encode_end(self):
pass
class RleMixin:
RLE_BYTE = 0
RLE_SEQ = 1
def __init__(self):
self.state = self.RLE_BYTE
self.count = 0
self.prev_byte = None
def eq_prev_byte(self, byte):
if self.prev_byte is None:
return False
return byte == self.prev_byte
def encode_byte(self, byte):
if self.state == self.RLE_BYTE:
self.write(byte)
if self.eq_prev_byte(byte):
self.state = self.RLE_SEQ
self.count = 0
else:
self.prev_byte = byte
elif self.state == self.RLE_SEQ:
if self.eq_prev_byte(byte):
self.count += 1
if self.count == 255:
self.write(self.count)
self.prev_byte = None
self.state = self.RLE_BYTE
else:
self.write(self.count)
self.write(byte)
self.prev_byte = byte
self.state = self.RLE_BYTE
def encode_end(self):
if self.state == self.RLE_SEQ:
self.write(self.count)
class Lz4Mixin:
def __init__(self):
self.buffer = []
def encode_byte(self,byte):
self.buffer.append(byte)
def encode_end(self):
compressed_data = lz4.block.compress(bytes(self.buffer), compression=12, mode='high_compression', store_size=False)
data_len = len(compressed_data).to_bytes(4, byteorder='little')
for b in data_len:
self.write(b)
for b in compressed_data:
self.write(b)
class ImageEncoder:
def __init__(self, filename, size_format, reverse=False):
self.f = open(filename, "w")
self.size_format = size_format
self.reverse = reverse
def write(self, value):
if isinstance(value, bytes):
value = int.from_bytes(value, 'little')
self.f.write("0x%02x," % value)
def write_size(self, width, height):
if self.size_format == 2:
self.f.write("%d,%d,%d,%d,\n" % (width % 256, width // 256, height % 256, height // 256))
else:
self.f.write("%d,%d,\n" % (width, height))
def encode_1bit(self, image, rows):
image = image.convert(mode='1')
width, height = image.size
self.write_size(width, height // rows)
for y in range(0, height, 8):
for x in range(width):
value = 0
for z in range(8):
if y + z < height:
if image.format == "XBM":
if image.getpixel((x, y + z)) > 0:
value += 1 << z
else:
if image.getpixel((x, y + z)) == 0:
value += 1 << z
self.encode_byte(value)
self.f.write("\n")
self.encode_end()
def encode_4bits(self, image):
image = image.convert(mode='L')
width, height = image.size
self.write_size(width, height)
for y in range(0, height, 2):
for x in range(width):
value = 0xFF
gray1 = self.get_pixel(image, x, y)
if y + 1 < height:
gray2 = self.get_pixel(image, x, y + 1)
else:
gray2 = 255
for i in range(4):
if gray1 & (1 << (4 + i)):
value -= 1 << i
if gray2 & (1 << (4 + i)):
value -= 1 << (4 + i)
self.encode_byte(value)
self.f.write("\n")
self.encode_end()
def encode_8bits(self, image):
image = image.convert(mode='L')
width, height = image.size
self.write_size(width, height)
for y in range(height):
for x in range(width):
value = self.get_pixel(image, x, y)
value = 0xFF - value
self.encode_byte(value)
self.encode_end()
def encode_5_6_5(self, image):
width, height = image.size
self.write_size(width, height)
for y in range(height):
for x in range(width):
pixel = self.get_pixel(image, x, y)
val = ((pixel[0] >> 3) << 11) + ((pixel[1] >> 2) << 5) + ((pixel[2] >> 3) << 0)
self.encode_byte(val & 255)
self.encode_byte(val >> 8)
self.encode_end()
def encode_4_4_4_4(self, image):
width, height = image.size
self.write_size(width, height)
for y in range(height):
for x in range(width):
pixel = self.get_pixel(image, x, y)
val = ((pixel[3] // 16) << 12) + ((pixel[0] // 16) << 8) + ((pixel[1] // 16) << 4) + ((pixel[2] // 16) << 0)
self.encode_byte(val & 255)
self.encode_byte(val >> 8)
self.encode_end()
def get_pixel(self, image, x, y):
if self.reverse:
return image.getpixel((image.width - x - 1, image.height - y - 1))
else:
return image.getpixel((x, y))
@staticmethod
def create(filename, size_format=1, reverse=False, encode_mixin=RawMixin):
class ResultClass(ImageEncoder, encode_mixin):
def __init__(self, *args, **kwargs):
ImageEncoder.__init__(self, *args, **kwargs)
encode_mixin.__init__(self)
return ResultClass(filename, size_format, reverse)
def main():
parser = argparse.ArgumentParser(description='Bitmaps encoder')
parser.add_argument('input', action="store", help="Input file name")
parser.add_argument('output', action="store", help="Output file name")
parser.add_argument('--format', action="store", help="Output format")
parser.add_argument("--reverse", help="Invert pixels order", action="store_true")
parser.add_argument("--rle", help="Enable RLE compression", action="store_true")
parser.add_argument("--lz4", help="Enable RLE compression", action="store_true")
parser.add_argument("--rows", help="Image rows count (for 1bit format)", type=int, default=1)
parser.add_argument("--size-format", help="Header image size format (1 or 2 bytes)", type=int, default=1)
args = parser.parse_args()
image = Image.open(args.input)
output = args.output
byte_encoder = RawMixin
if args.rle:
byte_encoder = RleMixin
if args.lz4:
byte_encoder = Lz4Mixin
encoder = ImageEncoder.create(output, args.size_format, args.reverse, byte_encoder)
if args.format == "1bit":
encoder.encode_1bit(image, args.rows)
elif args.format == "4bits":
encoder.encode_4bits(image)
elif args.format == "8bits":
encoder.encode_8bits(image)
elif args.format == "4/4/4/4":
encoder.encode_4_4_4_4(image)
elif args.format == "5/6/5":
encoder.encode_5_6_5(image)
if __name__ == "__main__":
main()