Merge pull request #8442 from opentx/raphaelcoeffic/mainscreens

2.4: main screen improvements
This commit is contained in:
Bertrand Songis
2021-04-21 14:36:09 +02:00
committed by GitHub
99 changed files with 2509 additions and 1926 deletions
+4 -37
View File
@@ -1,29 +1,9 @@
sudo: required
dist: xenial
language: cpp
compiler: gcc
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-sdk-team/ppa'
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:beineri/opt-qt571-xenial'
update: true
packages:
- curl
- libmpfr4
- libmpc3
- libfox-1.6-dev
- libgtest-dev
- lib32stdc++6
- libclang-common-6.0-dev
- clang-6.0
- python3-pip
services:
- docker
env:
global:
- QT_BASE=57
matrix:
- FLAVOR=COMPANION
- FLAVOR=ARM9X
@@ -34,20 +14,7 @@ env:
- FLAVOR=COLORLCD
before_install:
- wget --quiet https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2
- tar xjf gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2
- sudo mv gcc-arm-none-eabi-6-2017-q2-update /opt/gcc-arm-none-eabi
- sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-gcc /usr/bin/arm-none-eabi-gcc
- sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-g++ /usr/bin/arm-none-eabi-g++
- sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-as /usr/bin/arm-none-eabi-as
- sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-objcopy /usr/bin/arm-none-eabi-objcopy
- sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-objdump /usr/bin/arm-none-eabi-objdump
- sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-size /usr/bin/arm-none-eabi-size
- sudo apt-get install --yes --force-yes -qq qt${QT_BASE}base qt${QT_BASE}multimedia qt${QT_BASE}svg qt${QT_BASE}tools; source /opt/qt${QT_BASE}/bin/qt${QT_BASE}-env.sh
- sudo ln -sf /usr/bin/python3 /usr/bin/python
- python3 -m pip install pip==20.3.3
- sudo apt-get install python3-setuptools
- python3 -m pip install pillow==7.2.0 clang
- docker pull ghcr.io/raphaelcoeffic/opentx-commit-tests
script:
- ./tools/commit-tests.sh
- docker run --rm --env FLAVOR=$FLAVOR -v $(pwd):/src ghcr.io/raphaelcoeffic/opentx-commit-tests
+4
View File
@@ -21,6 +21,10 @@
#include "opentx.h"
#include <math.h>
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
extern RTOS_MUTEX_HANDLE audioMutex;
const int16_t sineValues[] =
+1
View File
@@ -25,6 +25,7 @@
#include <string.h>
#include "ff.h"
#include "opentx_types.h"
#include "dataconstants.h"
/*
Implements a bit field, number of bits is set by the template,
+6
View File
@@ -21,6 +21,12 @@
#include "opentx.h"
#include "io/frsky_firmware_update.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#else
#include "libopenui/src/libopenui_file.h"
#endif
#if defined(LOG_BLUETOOTH)
extern FIL g_bluetoothFile;
#endif
+4
View File
@@ -20,6 +20,10 @@
#include "opentx.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
int8_t * curveEnd[MAX_CURVES];
void loadCurves()
+3 -3
View File
@@ -553,11 +553,11 @@ typedef uint8_t swarnenable_t;
#define LAYOUT_ID_LEN 10
PACK(struct CustomScreenData {
char LayoutId[LAYOUT_ID_LEN];
Layout::PersistentData layoutData;
LayoutPersistentData layoutData;
});
#define CUSTOM_SCREENS_DATA \
NOBACKUP(CustomScreenData screenData[MAX_CUSTOM_SCREENS]); \
NOBACKUP(TopBar::PersistentData topbarData); \
NOBACKUP(TopBarPersistentData topbarData); \
NOBACKUP(uint8_t view);
#else
#define CUSTOM_SCREENS_DATA \
@@ -921,7 +921,7 @@ static inline void check_struct()
CHKSIZE(ModelHeader, 31);
CHKSIZE(CurveHeader, 4);
CHKSIZE(CustomScreenData, 850);
CHKSIZE(TopBar::PersistentData, 300);
CHKTYPE(TopBarPersistentData, 300);
#elif defined(PCBNV14)
// TODO
#elif defined(PCBSKY9X)
+9
View File
@@ -53,6 +53,7 @@ set(GUI_SRC
radio_diaganas.cpp
radio_calibration.cpp
view_main.cpp
view_main_decoration.cpp
view_channels.cpp
view_logical_switches.cpp
view_statistics.cpp
@@ -95,10 +96,18 @@ set(SRC
storage/modelslist.cpp
)
option(WINDOWS_INSPECT_BORDERS "Draw bounding boxes around windows" OFF)
if(WINDOWS_INSPECT_BORDERS)
add_definitions(-DWINDOWS_INSPECT_BORDERS)
endif()
# includes libopenui
include(${RADIO_SRC_DIR}/thirdparty/libopenui/src/CMakeLists.txt)
include_directories(gui/libopenui)
add_definitions(-DLIBOPENUI)
foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC})
set(SRC ${SRC} thirdparty/libopenui/src/${LIBOPENUI_FILE})
endforeach()
+4
View File
@@ -67,4 +67,8 @@
#define SCROLLBOX_COLOR COLOR(SCROLLBOX_COLOR_INDEX)
#define BATTERY_CHARGE_COLOR COLOR(BATTERY_CHARGE_COLOR_INDEX)
#if defined(WINDOWS_INSPECT_BORDERS)
#define WINDOWS_INSPECT_BORDER_COLOR LINE_COLOR
#endif
#endif // _COLORS_H_
+1
View File
@@ -21,6 +21,7 @@
#include "curveedit.h"
#include "keyboard_curve.h"
#include "opentx.h" // TODO for applyCustomCurve
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
+1 -1
View File
@@ -374,7 +374,7 @@ void drawSourceCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, source_t sou
{
if (source >= MIXSRC_FIRST_TELEM) {
source = (source-MIXSRC_FIRST_TELEM) / 3;
drawSensorCustomValue(dc, y, source, value, flags);
drawSensorCustomValue(dc, x, y, source, value, flags);
}
else if (source >= MIXSRC_FIRST_TIMER || source == MIXSRC_TX_TIME) {
// TODO if (value < 0) flags |= BLINK|INVERS;
@@ -21,6 +21,7 @@
#include "fullscreen_dialog.h"
#include "mainwindow.h"
#include "opentx.h"
#include "libopenui.h"
FullScreenDialog::FullScreenDialog(uint8_t type, std::string title, std::string message, std::string action, const std::function<void(void)> & confirmHandler):
FormGroup(MainWindow::instance(), {0, 0, LCD_W, LCD_H}, OPAQUE),
+1 -1
View File
@@ -47,7 +47,7 @@ void drawCurveHorizontalScale();
void drawCurveCoord(int x, int y, const char * text, bool active=false);
void drawCurvePoint(int x, int y, LcdFlags color);
extern Layout * customScreens[MAX_CUSTOM_SCREENS];
extern WidgetsContainer * customScreens[MAX_CUSTOM_SCREENS];
void drawAlertBox(const char * title, const char * text, const char * action);
void showAlertBox(const char * title, const char * text, const char * action, uint8_t sound);
+1
View File
@@ -21,6 +21,7 @@
#define _GVAR_NUMBEREDIT_H_
#include "form.h"
#include "choice.h"
#include "numberedit.h"
#include "gvars.h"
+89 -181
View File
@@ -20,8 +20,8 @@
#include "opentx.h"
#include "view_main.h"
#include "layouts/trims.h"
#include "layouts/sliders.h"
WidgetsContainer * customScreens[MAX_CUSTOM_SCREENS] = {};
std::list<const LayoutFactory *> & getRegisteredLayouts()
{
@@ -46,7 +46,11 @@ const LayoutFactory * getLayoutFactory(const char * name)
return nullptr;
}
Layout * loadLayout(const char * name, Layout::PersistentData * persistentData)
//
// Loads a layout, but does not attach it to any window
//
WidgetsContainer *
loadLayout(const char * name, LayoutPersistentData * persistentData)
{
const LayoutFactory * factory = getLayoutFactory(name);
if (factory) {
@@ -55,13 +59,14 @@ Layout * loadLayout(const char * name, Layout::PersistentData * persistentData)
return nullptr;
}
//
// Detaches and deletes all custom screens
//
void deleteCustomScreens()
{
for (unsigned int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
auto& screen = customScreens[i];
for (auto& screen : customScreens) {
if (screen) {
screen->detach();
delete screen;
screen->deleteLater();
screen = nullptr;
}
}
@@ -75,206 +80,109 @@ void loadDefaultLayout()
auto& screenData = g_model.screenData[0];
if (screen == nullptr && defaultLayout != nullptr) {
strcpy(screenData.LayoutId, defaultLayout->getId());
screen = defaultLayout->create(&screenData.layoutData);
//
// TODO:
// -> attach a few default widgets
// - ModelBmp
// - Timer
// - ???
//
if (screen) {
screen->attach(ViewMain::instance());
}
}
}
//
// Loads and attaches all configured custom screens
//
void loadCustomScreens()
{
for (unsigned int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
unsigned i = 0;
auto viewMain = ViewMain::instance();
while (i < MAX_CUSTOM_SCREENS) {
auto& screen = customScreens[i];
screen = loadLayout(g_model.screenData[i].LayoutId,
&g_model.screenData[i].layoutData);
if (screen) {
screen->attach(ViewMain::instance());
if (!screen) {
// no more layouts
break;
}
// layout is ok, let's add it
screen->attach(viewMain);
viewMain->setMainViewsCount(i + 1);
screen->setLeft(viewMain->getMainViewLeftPos(i));
i++;
}
auto topbar = viewMain->getTopbar();
// TODO: how to reload this guy?
topbar->load();
viewMain->setCurrentMainView(0);
viewMain->updateTopbarVisibility();
}
void Layout::decorate()
//
// Creates a new customer screen from factory:
// - the old screen is detached & deleted (including children)
// - new screen is configured into g_model
// - the new screen is returned (not attached)
//
WidgetsContainer *
createCustomScreen(const LayoutFactory* factory, unsigned customScreenIndex)
{
// check if deco setting are still up-to-date
uint8_t checkSettings =
(hasTopbar() ? 1 << 0 : 0) |
(hasSliders() ? 1 << 1 : 0) |
(hasTrims() ? 1 << 2 : 0) |
(hasFlightMode() ? 1 << 3 : 0);
if (!factory || (customScreenIndex >= MAX_CUSTOM_SCREENS))
return nullptr;
if (checkSettings == decorationSettings) {
// everything ok, exit!
if (customScreens[customScreenIndex]) {
customScreens[customScreenIndex]->deleteLater(true, false);
delete customScreens[customScreenIndex];
}
auto screen = factory->create(&g_model.screenData[customScreenIndex].layoutData);
customScreens[customScreenIndex] = screen;
if (screen) {
auto dst = g_model.screenData[customScreenIndex].LayoutId;
auto src = factory->getId();
strncpy(dst, src, sizeof(CustomScreenData::LayoutId));
return screen;
}
return nullptr;
}
void disposeCustomScreen(unsigned idx)
{
// move custom screen data
if (idx >= MAX_CUSTOM_SCREENS) {
return;
}
// kill all decorations & re-decorate
for (auto deco: decorations) {
deco->deleteLater();
}
decorations.clear();
topBar = nullptr;
auto dst = &g_model.screenData[idx];
auto src = dst + 1;
auto len = sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - idx - 1);
memmove(dst, src, len);
// effect a full redraw
invalidate();
// save settings
decorationSettings = checkSettings;
if (hasTopbar()) {
topBar = new TopBar(this);
topBar->load();
decorations.push_back(topBar);
}
if (hasSliders()) {
#if defined(HARDWARE_EXT1) || defined(HARDWARE_EXT2)
coord_t yOffset = (hasTrims() ? - TRIM_SQUARE_SIZE : 0) + (hasTopbar() ? TOPBAR_HEIGHT / 2 : 0);
#endif
decorations.push_back(
new MainViewHorizontalSlider(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return calibratedAnalogs[CALIBRATED_POT1]; })
);
if (IS_POT_MULTIPOS(POT2)) {
decorations.push_back(
new MainView6POS(this, {LCD_W / 2 - MULTIPOS_W / 2, LCD_H - TRIM_SQUARE_SIZE, MULTIPOS_W + 1, MULTIPOS_H},
[=] { return (1 + (potsPos[1] & 0x0f)); })
);
}
else if (IS_POT(POT2)) {
decorations.push_back(
new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return calibratedAnalogs[CALIBRATED_POT2]; })
);
}
#if defined(HARDWARE_POT3)
decorations.push_back(
new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return calibratedAnalogs[CALIBRATED_POT3]; })
);
#endif
#if defined(HARDWARE_EXT1)
if (IS_POT_SLIDER_AVAILABLE(EXT1)) {
decorations.push_back(
new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; })
);
decorations.push_back(
new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2},
[=] { return calibratedAnalogs[CALIBRATED_POT_EXT1]; })
);
}
else {
decorations.push_back(
new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar())},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; })
);
}
#endif
#if defined(HARDWARE_EXT2)
if (IS_POT_SLIDER_AVAILABLE(EXT2)) {
decorations.push_back(
new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE,
VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; })
);
decorations.push_back(
new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE,
VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2},
[=] { return calibratedAnalogs[CALIBRATED_POT_EXT2]; })
);
}
else {
decorations.push_back(
new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE,
VERTICAL_SLIDERS_HEIGHT(hasTopbar())},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; })
);
}
#endif
}
if (hasTrims()) {
#if defined(HARDWARE_POT3) || defined(HARDWARE_EXT1)
coord_t xOffset = hasSliders() ? TRIM_SQUARE_SIZE : 0;
#else
coord_t xOffset = 0;
#endif
coord_t yOffset = (hasTrims() ? - TRIM_SQUARE_SIZE : 0);
// Trim order TRIM_LH, TRIM_LV, TRIM_RV, TRIM_RH
// Left
decorations.push_back(
new MainViewHorizontalTrim(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE + yOffset, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return getTrimValue(mixerCurrentFlightMode, 0); })
);
// Right
decorations.push_back(
new MainViewHorizontalTrim(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE + yOffset, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return getTrimValue(mixerCurrentFlightMode, 3); })
);
// Left
decorations.push_back(
new MainViewVerticalTrim(this, {HMARGIN + xOffset, LCD_H /2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset + (hasTopbar() ? TOPBAR_HEIGHT / 2 : 0), TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar())},
[=] { return getTrimValue(mixerCurrentFlightMode, 1); })
);
// Right
decorations.push_back(
new MainViewVerticalTrim(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE - xOffset, LCD_H /2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset + (hasTopbar() ? TOPBAR_HEIGHT / 2 : 0), TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar())},
[=] { return getTrimValue(mixerCurrentFlightMode, 2); })
);
}
if (hasFlightMode()) {
decorations.push_back(
new DynamicText(this, {50, LCD_H - 4 - (hasSliders() ? 2 * TRIM_SQUARE_SIZE: TRIM_SQUARE_SIZE), LCD_W - 100, 20}, [=] {
return g_model.flightModeData[mixerCurrentFlightMode].name;
}, CENTERED)
);
}
dst = &g_model.screenData[MAX_CUSTOM_SCREENS - 1];
len = sizeof(CustomScreenData);
memset(dst, 0, len);
}
rect_t Layout::getMainZone(rect_t zone) const
LayoutFactory::LayoutFactory(const char * id, const char * name):
id(id),
name(name)
{
if (hasTopbar()) {
zone.y += MENU_HEADER_HEIGHT;
zone.h -= MENU_HEADER_HEIGHT;
}
registerLayout(this);
}
if (hasFlightMode() || hasTrims()) {
zone.h -= TRIM_SQUARE_SIZE;
}
if (hasSliders()) {
#if NUM_SLIDERS + NUM_POTS > 2
zone.h -= TRIM_SQUARE_SIZE;
#endif
zone.w -= 2 * TRIM_SQUARE_SIZE;
zone.x += TRIM_SQUARE_SIZE;
}
if (hasTrims()) {
zone.w -= 2 * TRIM_SQUARE_SIZE;
zone.x += TRIM_SQUARE_SIZE;
}
if (zone.w % 2 != 0)
zone.w -= 1;
if (zone.h % 2 != 0)
zone.h -= 1;
return zone;
}
+21 -147
View File
@@ -22,174 +22,35 @@
#include <list>
#include "widgets_container.h"
#include "lcd.h"
#include "topbar.h"
#define MAX_LAYOUT_ZONES 10
#define MAX_LAYOUT_OPTIONS 10
constexpr coord_t TRIM_LINE_WIDTH = 8;
constexpr coord_t TRIM_SQUARE_SIZE = 17;
constexpr coord_t MAIN_ZONE_BORDER = 10;
constexpr uint32_t LAYOUT_REFRESH = 1000 / 2; // 2 Hz
class LayoutFactory;
class BitmapBuffer;
#define LAYOUT_COMMON_OPTIONS \
{ STR_TOP_BAR, ZoneOption::Bool }, \
{ STR_FLIGHT_MODE, ZoneOption::Bool }, \
{ STR_SLIDERS, ZoneOption::Bool }, \
{ STR_TRIMS, ZoneOption::Bool }, \
{ STR_MIRROR, ZoneOption::Bool }
#define LAYOUT_OPTIONS_END \
{ nullptr, ZoneOption::Bool }
typedef WidgetsContainer<MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS> LayoutBase;
class Layout: public LayoutBase
{
friend class LayoutFactory;
public:
// Common 'ZoneOptionValue's among all layouts
enum {
OPTION_TOPBAR = 0,
OPTION_FM,
OPTION_SLIDERS,
OPTION_TRIMS,
OPTION_MIRRORED,
OPTION_LAST_DEFAULT=OPTION_MIRRORED
};
Layout(const LayoutFactory * factory, PersistentData * persistentData):
LayoutBase({0, 0, LCD_W, LCD_H}, persistentData),
factory(factory)
{
decorate();
}
void create() override
{
setOptionValue(OPTION_TOPBAR, OPTION_VALUE_BOOL(true));
setOptionValue(OPTION_FM, OPTION_VALUE_BOOL(true));
setOptionValue(OPTION_SLIDERS, OPTION_VALUE_BOOL(true));
setOptionValue(OPTION_TRIMS, OPTION_VALUE_BOOL(true));
setOptionValue(OPTION_MIRRORED, OPTION_VALUE_BOOL(false));
decorate();
}
inline const LayoutFactory * getFactory() const
{
return factory;
}
void checkEvents() override
{
LayoutBase::checkEvents();
decorate();
}
bool hasTopbar() const {
return getOptionValue(OPTION_TOPBAR)->boolValue;
}
bool hasFlightMode() const {
return getOptionValue(OPTION_FM)->boolValue;
}
bool hasSliders() const {
return getOptionValue(OPTION_SLIDERS)->boolValue;
}
bool hasTrims() const {
return getOptionValue(OPTION_TRIMS)->boolValue;
}
bool isMirrored() const {
return getOptionValue(OPTION_MIRRORED)->boolValue;
}
protected:
const LayoutFactory * factory;
TopBar * topBar = nullptr;
uint8_t decorationSettings = 0;
std::list<Window*> decorations;
// adds trims, sliders, pots, etc...
void decorate();
// get the Zone rect without decoration
rect_t getMainZone(rect_t zone) const;
};
void registerLayout(const LayoutFactory * factory);
typedef WidgetsContainerPersistentData<MAX_LAYOUT_ZONES,MAX_LAYOUT_OPTIONS> LayoutPersistentData;
class LayoutFactory
{
public:
LayoutFactory(const char * id, const char * name):
id(id),
name(name)
{
registerLayout(this);
}
LayoutFactory(const char * id, const char * name);
const char * getId() const { return id; }
const char * getName() const { return name; }
virtual void drawThumb(BitmapBuffer * dc, uint16_t x, uint16_t y, LcdFlags flags) const = 0;
virtual const ZoneOption * getOptions() const = 0;
virtual Layout * create(Layout::PersistentData * persistentData) const = 0;
virtual Layout * load(Layout::PersistentData * persistentData) const = 0;
virtual WidgetsContainer * create(LayoutPersistentData * persistentData) const = 0;
virtual WidgetsContainer * load(LayoutPersistentData * persistentData) const = 0;
protected:
const char * id;
const char * name;
};
template<class T>
class BaseLayoutFactory: public LayoutFactory
{
public:
BaseLayoutFactory(const char * id, const char * name, const uint8_t * bitmap, const ZoneOption * options):
LayoutFactory(id, name),
bitmap(bitmap),
options(options)
{
}
void drawThumb(BitmapBuffer * dc, uint16_t x, uint16_t y, uint32_t flags) const override
{
dc->drawBitmapPattern(x, y, bitmap, flags);
}
const ZoneOption * getOptions() const override
{
return options;
}
Layout * create(Layout::PersistentData * persistentData) const override
{
Layout * layout = new T(this, persistentData);
if (layout) {
layout->create();
}
return layout;
}
Layout * load(Layout::PersistentData * persistentData) const override
{
Layout * layout = new T(this, persistentData);
if (layout) {
layout->load();
}
return layout;
}
protected:
const uint8_t * bitmap;
const ZoneOption * options;
};
Layout * loadLayout(const char * name, Layout::PersistentData * persistentData);
WidgetsContainer * loadLayout(const char * name, LayoutPersistentData * persistentData);
// intented for new models
void loadDefaultLayout();
@@ -197,4 +58,17 @@ void loadDefaultLayout();
// intended for existing models
void loadCustomScreens();
// delete all custom screens from memory
void deleteCustomScreens();
WidgetsContainer *
createCustomScreen(const LayoutFactory* factory, unsigned customScreenIndex);
// Remove custom screen from the model
void disposeCustomScreen(unsigned idx);
// Layout must register to be found
void registerLayout(const LayoutFactory * factory);
// List of registered layout factories
std::list<const LayoutFactory *> & getRegisteredLayouts();
+3 -7
View File
@@ -18,10 +18,8 @@
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "trims.h"
constexpr coord_t border = 10;
#include "layout.h"
#include "layout_factory_impl.h"
const uint8_t LBM_LAYOUT_1x1[] = {
#include "mask_layout1x1.lbm"
@@ -47,9 +45,7 @@ class Layout1x1: public Layout
rect_t getZone(unsigned int index) const override
{
rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border});
return zone;
return getMainZone();
}
};
+3 -6
View File
@@ -18,11 +18,8 @@
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "sliders.h"
#include "trims.h"
constexpr coord_t border = 10;
#include "layout.h"
#include "layout_factory_impl.h"
const uint8_t LBM_LAYOUT_2P1[] = {
#include "mask_layout2+1.lbm"
@@ -48,7 +45,7 @@ class Layout2P1: public Layout
rect_t getZone(unsigned int index) const override
{
rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border});
rect_t zone = getMainZone();
if (index == 0) {
return {isMirrored() ? zone.x : zone.x + zone.w / 2, zone.y,zone.w / 2, zone.h};
+3 -6
View File
@@ -18,11 +18,8 @@
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "sliders.h"
#include "trims.h"
constexpr coord_t border = 10;
#include "layout.h"
#include "layout_factory_impl.h"
const uint8_t LBM_LAYOUT_2x1[] = {
#include "mask_layout2x1.lbm"
@@ -48,7 +45,7 @@ class Layout2x1: public Layout
rect_t getZone(unsigned int index) const override
{
rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border});
rect_t zone = getMainZone();
if (index == 0) {
return {isMirrored() ? zone.x + zone.w / 2 : zone.x, zone.y, zone.w / 2, zone.h};
+3 -6
View File
@@ -18,11 +18,8 @@
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "sliders.h"
#include "trims.h"
constexpr coord_t border = 10;
#include "layout.h"
#include "layout_factory_impl.h"
const uint8_t LBM_LAYOUT_2x2[] = {
#include "mask_layout2x2.lbm"
@@ -48,7 +45,7 @@ class Layout2x2: public Layout
rect_t getZone(unsigned int index) const override
{
rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border});
rect_t zone = getMainZone();
zone.w /= 2;
zone.h /= 2;
+8 -9
View File
@@ -18,9 +18,8 @@
* GNU General Public License for more details.
*/
#include "opentx.h"
constexpr coord_t border = 10;
#include "layout.h"
#include "layout_factory_impl.h"
const uint8_t LBM_LAYOUT_2x4[] = {
#include "mask_layout2x4.lbm"
@@ -40,7 +39,7 @@ class Layout2x4: public Layout
public:
enum {
OPTION_PANEL1_BACKGROUND = OPTION_LAST_DEFAULT + 1,
OPTION_PANEL1_BACKGROUND = LAYOUT_OPTION_LAST_DEFAULT + 1,
OPTION_PANEL1_COLOR,
OPTION_PANEL2_BACKGROUND,
OPTION_PANEL2_COLOR
@@ -54,10 +53,10 @@ class Layout2x4: public Layout
void create() override
{
Layout::create();
setOptionValue(OPTION_PANEL1_BACKGROUND, OPTION_VALUE_BOOL(true));
setOptionValue(OPTION_PANEL1_COLOR, OPTION_VALUE_UNSIGNED( RGB(77,112,203)));
setOptionValue(OPTION_PANEL2_BACKGROUND, OPTION_VALUE_BOOL(true));
setOptionValue(OPTION_PANEL2_COLOR, OPTION_VALUE_UNSIGNED( RGB(77,112,203)));
getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue = true;
getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue = RGB(77,112,203);
getOptionValue(OPTION_PANEL2_BACKGROUND)->boolValue = true;
getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue = RGB(77,112,203);
}
unsigned int getZonesCount() const override
@@ -67,7 +66,7 @@ class Layout2x4: public Layout
rect_t getZone(unsigned int index) const override
{
rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border});
rect_t zone = getMainZone();
zone.w /= 2;
zone.h /= 4;
@@ -0,0 +1,122 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "layout_factory_impl.h"
#include "layouts/trims.h"
#include "layouts/sliders.h"
#include "view_main_decoration.h"
#include "view_main.h"
Layout::Layout(const LayoutFactory * factory, PersistentData * persistentData):
LayoutBase({0, 0, LCD_W, LCD_H}, persistentData),
factory(factory),
decoration(new ViewMainDecoration(this, getRect()))
{
adjustLayout();
}
void Layout::create()
{
memset(persistentData, 0, sizeof(PersistentData));
getOptionValue(LAYOUT_OPTION_TOPBAR)->boolValue = true;
getOptionValue(LAYOUT_OPTION_FM)->boolValue = true;
getOptionValue(LAYOUT_OPTION_SLIDERS)->boolValue = true;
getOptionValue(LAYOUT_OPTION_TRIMS)->boolValue = true;
getOptionValue(LAYOUT_OPTION_MIRRORED)->boolValue = false;
}
#if defined(DEBUG_WINDOWS)
void Layout::paint(BitmapBuffer * dc)
{
TRACE_WINDOWS("# painting -> %s", getWindowDebugString().c_str());
LayoutBase::paint(dc);
}
#endif
void Layout::checkEvents()
{
LayoutBase::checkEvents();
adjustLayout();
uint32_t now = RTOS_GET_MS();
if (now - lastRefresh >= LAYOUT_REFRESH) {
lastRefresh = now;
invalidate();
#if defined(DEBUG_WINDOWS)
TRACE_WINDOWS("# %s refresh: %s", factory->getId(), getWindowDebugString().c_str());
#endif
}
}
void Layout::setTrimsVisible(bool visible)
{
decoration->setTrimsVisible(visible);
}
void Layout::setSlidersVisible(bool visible)
{
decoration->setSlidersVisible(visible);
}
void Layout::setFlightModeVisible(bool visible)
{
decoration->setFlightModeVisible(visible);
}
void Layout::adjustLayout()
{
// Check if deco setting are still up-to-date
uint8_t checkSettings =
(hasTopbar() ? 1 << 0 : 0) |
(hasSliders() ? 1 << 1 : 0) |
(hasTrims() ? 1 << 2 : 0) |
(hasFlightMode() ? 1 << 3 : 0);
if (checkSettings == decorationSettings) {
// everything ok, exit!
return;
}
// Save settings
decorationSettings = checkSettings;
// Set visible decoration
setSlidersVisible(hasSliders());
setTrimsVisible(hasTrims());
setFlightModeVisible(hasFlightMode());
// Re-compute positions
decoration->adjustDecoration();
// and update relevant windows
updateZones();
// probably not needed
//invalidate();
}
rect_t Layout::getMainZone() const
{
rect_t zone = decoration->getMainZone();
return ViewMain::instance()->getMainZone(zone, hasTopbar());
}
@@ -0,0 +1,170 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 <list>
#include "audio.h"
#include "translations.h"
#include "widgets/widgets_container_impl.h"
#include "layout.h"
#define LAYOUT_COMMON_OPTIONS \
{ STR_TOP_BAR, ZoneOption::Bool }, \
{ STR_FLIGHT_MODE, ZoneOption::Bool }, \
{ STR_SLIDERS, ZoneOption::Bool }, \
{ STR_TRIMS, ZoneOption::Bool }, \
{ STR_MIRROR, ZoneOption::Bool }
#define LAYOUT_OPTIONS_END \
{ nullptr, ZoneOption::Bool }
class ViewMainDecoration;
typedef WidgetsContainerImpl<MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS> LayoutBase;
class Layout: public LayoutBase
{
friend class LayoutFactory;
public:
Layout(const LayoutFactory * factory, PersistentData * persistentData);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Layout";
}
void paint(BitmapBuffer * dc) override;
#endif
void create() override;
const LayoutFactory * getFactory() const
{
return factory;
}
void checkEvents() override;
bool hasTopbar() const {
return getOptionValue(LAYOUT_OPTION_TOPBAR)->boolValue;
}
bool hasFlightMode() const {
return getOptionValue(LAYOUT_OPTION_FM)->boolValue;
}
bool hasSliders() const {
return getOptionValue(LAYOUT_OPTION_SLIDERS)->boolValue;
}
bool hasTrims() const {
return getOptionValue(LAYOUT_OPTION_TRIMS)->boolValue;
}
bool isMirrored() const {
return getOptionValue(LAYOUT_OPTION_MIRRORED)->boolValue;
}
// Set decoration visibility
void setTrimsVisible(bool visible);
void setSlidersVisible(bool visible);
void setFlightModeVisible(bool visible);
// Updates settings for trims, sliders, pots, etc...
void adjustLayout() override;
protected:
const LayoutFactory * factory = nullptr;
ViewMainDecoration* decoration = nullptr;
// Decoration settings bitmask to detect updates
uint8_t decorationSettings = 0;
// Last time we refreshed the window
uint32_t lastRefresh = 0;
// Get the available space for widgets
rect_t getMainZone() const;
};
template<class T>
class BaseLayoutFactory: public LayoutFactory
{
public:
BaseLayoutFactory(const char * id, const char * name, const uint8_t * bitmap, const ZoneOption * options):
LayoutFactory(id, name),
bitmap(bitmap),
options(options)
{
}
void drawThumb(BitmapBuffer * dc, uint16_t x, uint16_t y, uint32_t flags) const override
{
dc->drawBitmapPattern(x, y, bitmap, flags);
}
const ZoneOption * getOptions() const override
{
return options;
}
Layout * create(Layout::PersistentData * persistentData) const override
{
initPersistentData(persistentData);
Layout * layout = new T(this, persistentData);
if (layout) {
layout->create();
}
return layout;
}
Layout * load(Layout::PersistentData * persistentData) const override
{
Layout * layout = new T(this, persistentData);
if (layout) {
layout->load();
}
return layout;
}
void initPersistentData(Layout::PersistentData * persistentData) const
{
memset(persistentData, 0, sizeof(Layout::PersistentData));
if (options) {
int i = 0;
for (const ZoneOption * option = options; option->name; option++, i++) {
TRACE("LayoutFactory::initPersistentData() setting option '%s'", option->name);
// TODO compiler bug? The CPU freezes ... persistentData->options[i++] = option->deflt;
memcpy(&persistentData->options[i].value, &option->deflt, sizeof(ZoneOptionValue));
persistentData->options[i].type = zoneValueEnumFromType(option->type);
}
}
}
protected:
const uint8_t * bitmap;
const ZoneOption * options;
};
@@ -0,0 +1,76 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "sliders.h"
#include "opentx.h"
void MainViewHorizontalSlider::paint(BitmapBuffer * dc)
{
// The ticks
int delta = (width() - TRIM_SQUARE_SIZE) / SLIDER_TICKS_COUNT;
coord_t x = TRIM_SQUARE_SIZE / 2;
for (uint8_t i = 0; i <= SLIDER_TICKS_COUNT; i++) {
if (i == 0 || i == SLIDER_TICKS_COUNT / 2 || i == SLIDER_TICKS_COUNT)
dc->drawSolidVerticalLine(x, 2, 13, DEFAULT_COLOR);
else
dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR);
x += delta;
}
// The square
x = width() - TRIM_SQUARE_SIZE - divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX);
drawTrimSquare(dc, x, 0, TRIM_BGCOLOR);
}
void MainView6POS::paint(BitmapBuffer * dc)
{
// The ticks
int delta = (width() - TRIM_SQUARE_SIZE) / (XPOTS_MULTIPOS_COUNT - 1);
coord_t x = TRIM_SQUARE_SIZE / 2;
for (uint8_t i = 0; i <= XPOTS_MULTIPOS_COUNT; i++) {
dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR);
x += delta;
}
// The square
auto value = 1 + (potsPos[idx] & 0x0f);
x = TRIM_SQUARE_SIZE / 2 + divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value -1) , 6);
drawTrimSquare(dc, x, 0, TRIM_BGCOLOR);
dc->drawNumber(x + 1, 0, value, FOCUS_COLOR);
}
void MainViewVerticalSlider::paint(BitmapBuffer * dc)
{
uint8_t slidersTick = height() / 5;
// The ticks
int delta = (height() - TRIM_SQUARE_SIZE) / slidersTick;
coord_t y = TRIM_SQUARE_SIZE / 2;
for (uint8_t i = 0; i <= slidersTick; i++) {
if (i == 0 || i == slidersTick / 2 || i == slidersTick)
dc->drawSolidHorizontalLine(2, y, 13, DEFAULT_COLOR);
else
dc->drawSolidHorizontalLine(4, y, 9, DEFAULT_COLOR);
y += delta;
}
// The square
y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX);
drawTrimSquare(dc, 0, y, TRIM_BGCOLOR);
}
+9 -65
View File
@@ -32,24 +32,21 @@ constexpr coord_t HMARGIN = 5;
constexpr coord_t HORIZONTAL_SLIDERS_WIDTH = SLIDER_TICKS_COUNT * 4 + TRIM_SQUARE_SIZE;
constexpr coord_t MULTIPOS_H = 20;
constexpr coord_t MULTIPOS_W = 50;
inline coord_t VERTICAL_SLIDERS_HEIGHT(bool topbar)
{
return topbar ? SLIDER_TICKS_COUNT * 4 + TRIM_SQUARE_SIZE : SLIDER_TICKS_COUNT * 5 + TRIM_SQUARE_SIZE;
}
constexpr coord_t VERTICAL_SLIDERS_HEIGHT = SLIDER_TICKS_COUNT * 4 + TRIM_SQUARE_SIZE;
class MainViewSlider : public Window
{
public:
MainViewSlider(Window * parent, const rect_t & rect, std::function<int16_t()> getValue):
MainViewSlider(Window * parent, const rect_t & rect, uint8_t idx):
Window(parent, rect),
getValue(std::move(getValue))
idx(idx)
{
}
void checkEvents() override
{
Window::checkEvents();
int16_t newValue = getValue();
int16_t newValue = calibratedAnalogs[idx];
if (value != newValue) {
value = newValue;
invalidate();
@@ -57,80 +54,27 @@ class MainViewSlider : public Window
}
protected:
std::function<int16_t()> getValue;
int8_t value = 0;
uint8_t idx;
int16_t value = 0;
};
class MainViewHorizontalSlider : public MainViewSlider
{
public:
using MainViewSlider::MainViewSlider;
void paint(BitmapBuffer * dc) override
{
// The ticks
int delta = (width() - TRIM_SQUARE_SIZE) / SLIDER_TICKS_COUNT;
coord_t x = TRIM_SQUARE_SIZE / 2;
for (uint8_t i = 0; i <= SLIDER_TICKS_COUNT; i++) {
if (i == 0 || i == SLIDER_TICKS_COUNT / 2 || i == SLIDER_TICKS_COUNT)
dc->drawSolidVerticalLine(x, 2, 13, DEFAULT_COLOR);
else
dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR);
x += delta;
}
// The square
auto value = - getValue();
x = width() - TRIM_SQUARE_SIZE - divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX);
drawTrimSquare(dc, x, 0, TRIM_BGCOLOR);
}
void paint(BitmapBuffer * dc) override;
};
class MainView6POS : public MainViewSlider
{
public:
using MainViewSlider::MainViewSlider;
void paint(BitmapBuffer * dc) override
{
// The ticks
int delta = (width() - TRIM_SQUARE_SIZE) / (XPOTS_MULTIPOS_COUNT - 1);
coord_t x = TRIM_SQUARE_SIZE / 2;
for (uint8_t i = 0; i <= XPOTS_MULTIPOS_COUNT; i++) {
dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR);
x += delta;
}
// The square
auto value = getValue();
x = TRIM_SQUARE_SIZE / 2 + divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value -1) , 6);
drawTrimSquare(dc, x, 0, TRIM_BGCOLOR);
dc->drawNumber(x + 1, 0, value, FOCUS_COLOR);
}
void paint(BitmapBuffer * dc) override;
};
class MainViewVerticalSlider : public MainViewSlider
{
public:
using MainViewSlider::MainViewSlider;
void paint(BitmapBuffer * dc) override
{
uint8_t slidersTick = height() / 5;
// The ticks
int delta = (height() - TRIM_SQUARE_SIZE) / slidersTick;
coord_t y = TRIM_SQUARE_SIZE / 2;
for (uint8_t i = 0; i <= slidersTick; i++) {
if (i == 0 || i == slidersTick / 2 || i == slidersTick)
dc->drawSolidHorizontalLine(2, y, 13, DEFAULT_COLOR);
else
dc->drawSolidHorizontalLine(4, y, 9, DEFAULT_COLOR);
y += delta;
}
// The square
auto value = getValue();
y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX);
drawTrimSquare(dc, 0, y, TRIM_BGCOLOR);
}
void paint(BitmapBuffer * dc) override;
};
@@ -0,0 +1,195 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "topbar_impl.h"
#include "opentx.h"
const char * const STR_MONTHS[] = TR_MONTHS;
constexpr uint32_t TOPBAR_REFRESH = 1000 / 2; // 2 Hz
TopbarImpl::TopbarImpl(Window * parent) :
TopbarImplBase({0, 0, LCD_W, MENU_HEADER_HEIGHT}, &g_model.topbarData)
{
attach(parent);
}
unsigned int TopbarImpl::getZonesCount() const
{
return MAX_TOPBAR_ZONES;
}
rect_t TopbarImpl::getZone(unsigned int index) const
{
return {
coord_t(49 + (TOPBAR_ZONE_WIDTH + 2 * TOPBAR_ZONE_MARGIN) * index),
TOPBAR_ZONE_MARGIN,
TOPBAR_ZONE_WIDTH,
TOPBAR_HEIGHT
};
}
void TopbarImpl::setVisible(float visible) // 0.0 -> 1.0
{
if (visible == 0.0) {
setTop(-(int)MENU_HEADER_HEIGHT);
}
else if (visible == 1.0) {
setTop(0);
}
else if (visible > 0.0 && visible < 1.0){
float top = - (float)MENU_HEADER_HEIGHT * (1.0 - visible);
setTop((coord_t)top);
}
}
coord_t TopbarImpl::getVisibleHeight(float visible) const // 0.0 -> 1.0
{
if (visible == 0.0) {
return 0;
}
else if (visible == 1.0) {
return MENU_HEADER_HEIGHT;
}
float h = (float)MENU_HEADER_HEIGHT * visible;
return (coord_t)h;
}
void TopbarImpl::paint(BitmapBuffer * dc)
{
dc->drawSolidFilledRect(0, 0, width(), height(), MENU_BGCOLOR);
OpenTxTheme::instance()->drawTopLeftBitmap(dc);
struct gtm t;
gettime(&t);
char str[10];
sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]);
dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, MENU_COLOR);
dc->drawText(DATETIME_MIDDLE, DATETIME_LINE1, str, FONT(XS) | CENTERED| MENU_COLOR);
getTimerString(str, getValue(MIXSRC_TX_TIME));
dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | MENU_COLOR);
// USB icon
if (usbPlugged()) {
dc->drawBitmapPattern(LCD_W - 98, 8, LBM_TOPMENU_USB, MENU_COLOR);
}
// Logs
if (isFunctionActive(FUNCTION_LOGS) && !usbPlugged() && BLINK_ON_PHASE) {
dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, MENU_COLOR);
}
// RSSI
const uint8_t rssiBarsValue[] = {30, 40, 50, 60, 80};
const uint8_t rssiBarsHeight[] = {5, 10, 15, 21, 31};
for (unsigned int i = 0; i < DIM(rssiBarsHeight); i++) {
uint8_t height = rssiBarsHeight[i];
dc->drawSolidFilledRect(LCD_W - 90 + i * 6, 38 - height, 4, height, TELEMETRY_RSSI() >= rssiBarsValue[i] ? MENU_COLOR : MENU_TITLE_DISABLE_COLOR);
}
#if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) {
dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, MENU_COLOR);
}
#endif
/* Audio volume */
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_SCALE, MENU_TITLE_DISABLE_COLOR);
if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_0, MENU_COLOR);
else if (requiredSpeakerVolume < 7)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, MENU_COLOR);
else if (requiredSpeakerVolume < 13)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, MENU_COLOR);
else if (requiredSpeakerVolume < 19)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, MENU_COLOR);
else
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, MENU_COLOR);
/* Tx battery */
uint8_t bars = GET_TXBATT_BARS(5);
#if defined(USB_CHARGER)
if (usbChargerLed()) {
dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT_CHARGING, MENU_COLOR);
}
else {
dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR);
}
#else
dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR);
#endif
for (unsigned int i = 0; i < 5; i++) {
dc->drawSolidFilledRect(LCD_W - 128 + 4 * i, 30, 2, 8, i >= bars ? MENU_TITLE_DISABLE_COLOR : MENU_COLOR);
}
#if 0
// Radio battery - TODO
// drawValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|FONT(XS)|MENU_COLOR);
// lcdDrawSolidRect(300, 3, 20, 50, MENU_COLOR);
// lcdDrawRect(batt_icon_x+FW, BAR_Y+1, 13, 7);
// lcdDrawSolidVerticalLine(batt_icon_x+FW+13, BAR_Y+2, 5);
// Rx battery
if (g_model.voltsSource) {
TelemetryItem & item = telemetryItems[g_model.voltsSource-1];
if (item.isAvailable()) {
int32_t value = item.value;
TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1];
LcdFlags att = 0;
if (sensor.prec == 2) {
att |= PREC1;
value /= 10;
}
else if (sensor.prec == 1) {
att |= PREC1;
}
att |= (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR);
lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR);
lcdDrawText(ALTITUDE_X+PADDING, VOLTS_Y+2, "Voltage", att);
drawValueWithUnit(ALTITUDE_X+PADDING, VOLTS_Y+12, value, UNIT_VOLTS, FONT(XL)|LEFT|att);
}
}
// Model altitude
if (g_model.frsky.altitudeSource) {
TelemetryItem & item = telemetryItems[g_model.frsky.altitudeSource-1];
if (item.isAvailable()) {
int32_t value = item.value;
TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1];
if (sensor.prec) value /= sensor.prec == 2 ? 100 : 10;
LcdFlags att = (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR);
lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR);
lcdDrawText(ALTITUDE_X+PADDING, ALTITUDE_Y+2, "Alt", att);
drawValueWithUnit(ALTITUDE_X+PADDING, ALTITUDE_Y+12, value, UNIT_METERS, FONT(XL)|LEFT|att);
}
}
#endif
}
void TopbarImpl::checkEvents()
{
uint32_t now = RTOS_GET_MS();
if (now - lastRefresh >= TOPBAR_REFRESH) {
lastRefresh = now;
invalidate();
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "topbar.h"
#include "layouts/layout_factory_impl.h"
typedef WidgetsContainerImpl<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS> TopbarImplBase;
class TopbarImpl:
public TopbarImplBase,
public Topbar
{
public:
explicit TopbarImpl(Window * parent);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "TopBar";
}
#endif
void load() override
{
TopbarImplBase::load();
}
unsigned int getZonesCount() const override;
rect_t getZone(unsigned int index) const override;
void setVisible(float visible);
coord_t getVisibleHeight(float visible) const; // 0.0 -> 1.0
void paint(BitmapBuffer * dc) override;
void checkEvents() override;
protected:
uint32_t lastRefresh = 0;
};
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "trims.h"
#include "opentx.h"
void MainViewTrim::checkEvents()
{
Window::checkEvents();
int8_t newValue = getTrimValue(mixerCurrentFlightMode, idx);
if (value != newValue) {
value = newValue;
invalidate();
}
}
void MainViewHorizontalTrim::paint(BitmapBuffer * dc)
{
int32_t trimMin, trimMax;
if (g_model.extendedTrims) {
trimMin = TRIM_EXTENDED_MIN;
trimMax = TRIM_EXTENDED_MAX;
}
else {
trimMin = TRIM_MIN;
trimMax = TRIM_MAX;
}
// Trim line
lcdSetColor(GREY); // TODO add a color
dc->drawSolidFilledRect(TRIM_SQUARE_SIZE / 2, (height() - TRIM_LINE_WIDTH - 1) / 2, width() - TRIM_SQUARE_SIZE + 1, TRIM_LINE_WIDTH, CUSTOM_COLOR);
// Trim square
coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin);
drawTrimSquare(dc, x, 0, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR);
// Trim value / small lines on the square
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) {
// TODO DISPLAY_TRIMS_CHANGE
dc->drawNumber(x + (TRIM_SQUARE_SIZE + 1) / 2, 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED);
}
else {
if (value >= 0) {
dc->drawSolidVerticalLine(x + 4, 3, 9, FOCUS_COLOR);
}
if (value <= 0) {
dc->drawSolidVerticalLine(x + 10, 3, 9, FOCUS_COLOR);
}
}
}
void MainViewVerticalTrim::paint(BitmapBuffer * dc)
{
int32_t trimMin, trimMax;
if (g_model.extendedTrims) {
trimMin = TRIM_EXTENDED_MIN;
trimMax = TRIM_EXTENDED_MAX;
}
else {
trimMin = TRIM_MIN;
trimMax = TRIM_MAX;
}
// Trim line
lcdSetColor(GREY); // TODO add a color
dc->drawSolidFilledRect((width() - TRIM_LINE_WIDTH) / 2, TRIM_SQUARE_SIZE / 2, TRIM_LINE_WIDTH, height() - TRIM_SQUARE_SIZE + 1, CUSTOM_COLOR);
// Trim square
coord_t y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin);
drawTrimSquare(dc, 0, y, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR);
// Trim value / small lines on the square
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) {
// TODO DISPLAY_TRIMS_CHANGE
dc->drawNumber((TRIM_SQUARE_SIZE + 1) / 2, y + 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED);
}
else {
if (value >= 0) {
dc->drawSolidHorizontalLine(3, y + 4, 9, FOCUS_COLOR);
}
if (value <= 0) {
dc->drawSolidHorizontalLine(3, y + 10, 9, FOCUS_COLOR);
}
}
}
+8 -84
View File
@@ -25,25 +25,17 @@
class MainViewTrim : public Window
{
public:
MainViewTrim(Window * parent, const rect_t & rect, std::function<int16_t()> getValue):
MainViewTrim(Window * parent, const rect_t & rect, uint8_t idx):
Window(parent, rect),
getValue(std::move(getValue))
idx(idx)
{
}
void checkEvents() override
{
Window::checkEvents();
int8_t newValue = getValue();
if (value != newValue) {
value = newValue;
invalidate();
}
}
void checkEvents() override;
protected:
std::function<int16_t()> getValue;
int8_t value = 0;
uint8_t idx;
int value = 0;
};
class MainViewHorizontalTrim : public MainViewTrim
@@ -51,41 +43,7 @@ class MainViewHorizontalTrim : public MainViewTrim
public:
using MainViewTrim::MainViewTrim;
void paint(BitmapBuffer * dc) override
{
int32_t trimMin, trimMax;
if (g_model.extendedTrims) {
trimMin = TRIM_EXTENDED_MIN;
trimMax = TRIM_EXTENDED_MAX;
}
else {
trimMin = TRIM_MIN;
trimMax = TRIM_MAX;
}
// Trim line
lcdSetColor(GREY); // TODO add a color
dc->drawSolidFilledRect(TRIM_SQUARE_SIZE / 2, (height() - TRIM_LINE_WIDTH - 1) / 2, width() - TRIM_SQUARE_SIZE + 1, TRIM_LINE_WIDTH, CUSTOM_COLOR);
// Trim square
auto value = getValue();
coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin);
drawTrimSquare(dc, x, 0, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR);
// Trim value / small lines on the square
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) {
// TODO DISPLAY_TRIMS_CHANGE
dc->drawNumber(x + (TRIM_SQUARE_SIZE + 1) / 2, 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED);
}
else {
if (value >= 0) {
dc->drawSolidVerticalLine(x + 4, 3, 9, FOCUS_COLOR);
}
if (value <= 0) {
dc->drawSolidVerticalLine(x + 10, 3, 9, FOCUS_COLOR);
}
}
}
void paint(BitmapBuffer * dc) override;
};
class MainViewVerticalTrim : public MainViewTrim
@@ -93,39 +51,5 @@ class MainViewVerticalTrim : public MainViewTrim
public:
using MainViewTrim::MainViewTrim;
void paint(BitmapBuffer * dc) override
{
int32_t trimMin, trimMax;
if (g_model.extendedTrims) {
trimMin = TRIM_EXTENDED_MIN;
trimMax = TRIM_EXTENDED_MAX;
}
else {
trimMin = TRIM_MIN;
trimMax = TRIM_MAX;
}
// Trim line
lcdSetColor(GREY); // TODO add a color
dc->drawSolidFilledRect((width() - TRIM_LINE_WIDTH) / 2, TRIM_SQUARE_SIZE / 2, TRIM_LINE_WIDTH, height() - TRIM_SQUARE_SIZE + 1, CUSTOM_COLOR);
// Trim square
auto value = getValue();
coord_t y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin);
drawTrimSquare(dc, 0, y, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR);
// Trim value / small lines on the square
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) {
// TODO DISPLAY_TRIMS_CHANGE
dc->drawNumber((TRIM_SQUARE_SIZE + 1) / 2, y + 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED);
}
else {
if (value >= 0) {
dc->drawSolidHorizontalLine(3, y + 4, 9, FOCUS_COLOR);
}
if (value <= 0) {
dc->drawSolidHorizontalLine(3, y + 10, 9, FOCUS_COLOR);
}
}
}
};
void paint(BitmapBuffer * dc) override;
};
+1
View File
@@ -55,6 +55,7 @@
#include "curveedit.h"
#include "coloredit.h"
#include "draw_functions.h"
#include "textedits.h"
inline MessageDialog * createPopupInformation(const char * message)
{
+10 -3
View File
@@ -21,22 +21,29 @@
#include "opentx.h"
#include "menu_screen.h"
#include "screen_setup.h"
#include "view_main.h"
#include "storage/storage.h"
ScreenMenu::ScreenMenu():
TabsGroup(ICON_THEME)
{
updateTabs();
setCloseHandler([]{
ViewMain::instance()->updateTopbarVisibility();
storageDirty(EE_MODEL);
});
}
void ScreenMenu::updateTabs()
{
removeAllTabs();
addTab(new ScreenUserInterfacePage());
addTab(new ScreenUserInterfacePage(this));
for (int index = 0; index < MAX_CUSTOM_SCREENS; index++) {
if (customScreens[index]) {
auto tab = new ScreenSetupPage(this, customScreens[index], g_model.screenData[index]);
auto tab = new ScreenSetupPage(this, getTabs(), index);
std::string title(STR_MAIN_VIEW_X);
title.back() = index + '1';
tab->setTitle(title);
@@ -45,7 +52,7 @@ void ScreenMenu::updateTabs()
addTab(tab);
}
else {
addTab(new ScreenAddPage(this, index));
addTab(new ScreenAddPage(this, getTabs()));
break;
}
}
+2 -2
View File
@@ -67,7 +67,7 @@ class CurveEditWindow : public Page
// Name
new StaticText(window, grid.getFieldSlot(), STR_NAME);
grid.nextLine();
new TextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name));
new RadioTextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name));
grid.nextLine();
// Type
@@ -149,7 +149,7 @@ class CurveEditWindow : public Page
// Name
new StaticText(window, grid.getLabelSlot(), STR_NAME);
new TextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name));
new RadioTextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name));
grid.nextLine();
// Type
+2 -1
View File
@@ -20,6 +20,7 @@
#include "model_flightmodes.h"
#include "opentx.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
@@ -88,7 +89,7 @@ void ModelFlightModesPage::build(FormWindow * window)
// Flight mode name
new StaticText(group, grid.getLabelSlot(true), STR_NAME);
new TextEdit(group, grid.getFieldSlot(), g_model.flightModeData[i].name, LEN_FLIGHT_MODE_NAME);
new ModelTextEdit(group, grid.getFieldSlot(), g_model.flightModeData[i].name, LEN_FLIGHT_MODE_NAME);
grid.nextLine();
// Flight mode switch
+1 -1
View File
@@ -242,7 +242,7 @@ void GVarEditWindow::buildBody(FormWindow * window)
grid.spacer(8);
new StaticText(window, grid.getLabelSlot(), STR_NAME);
new TextEdit(window, grid.getFieldSlot(), gvar->name, LEN_GVAR_NAME);
new RadioTextEdit(window, grid.getFieldSlot(), gvar->name, LEN_GVAR_NAME);
grid.nextLine();
+4 -3
View File
@@ -21,6 +21,7 @@
#include "model_inputs.h"
#include "opentx.h"
#include "gvar_numberedit.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
@@ -185,7 +186,7 @@ class InputEditWindow: public Page
// Input Name
new StaticText(window, grid.getLabelSlot(), STR_INPUTNAME);
new TextEdit(window, grid.getFieldSlot(), g_model.inputNames[line->chn], sizeof(g_model.inputNames[line->chn]));
new ModelTextEdit(window, grid.getFieldSlot(), g_model.inputNames[line->chn], sizeof(g_model.inputNames[line->chn]));
grid.nextLine();
// Switch
@@ -207,7 +208,7 @@ class InputEditWindow: public Page
// Name
new StaticText(window, grid.getLabelSlot(), STR_EXPONAME);
new TextEdit(window, grid.getFieldSlot(), line->name, sizeof(line->name));
new ModelTextEdit(window, grid.getFieldSlot(), line->name, sizeof(line->name));
grid.nextLine();
// Source
@@ -527,4 +528,4 @@ void insertExpo(uint8_t idx, uint8_t input)
expo->weight = 100;
resumeMixerCalculations();
storageDirty(EE_MODEL);
}
}
+1 -1
View File
@@ -84,7 +84,7 @@ class MixEditWindow : public Page {
// Mix name
new StaticText(window, grid.getLabelSlot(), STR_MIXNAME);
new TextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name));
new RadioTextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name));
grid.nextLine();
// Source
+1 -1
View File
@@ -54,7 +54,7 @@ class OutputEditWindow : public Page {
// Name
new StaticText(window, grid.getLabelSlot(), STR_NAME);
new TextEdit(window, grid.getFieldSlot(), output->name, sizeof(output->name));
new RadioTextEdit(window, grid.getFieldSlot(), output->name, sizeof(output->name));
grid.nextLine();
// Offset
+1
View File
@@ -22,6 +22,7 @@
#include "model_select.h"
#include "opentx.h"
#include "storage/modelslist.h"
#include "libopenui.h"
#define CATEGORIES_WIDTH 120
#define MODELS_LEFT 123
+4 -4
View File
@@ -171,7 +171,7 @@ class RegisterDialog: public Dialog {
// Register ID
new StaticText(form, grid.getLabelSlot(), STR_REG_ID);
auto edit = new TextEdit(form, grid.getFieldSlot(), g_model.modelRegistrationID, sizeof(g_model.modelRegistrationID));
auto edit = new RadioTextEdit(form, grid.getFieldSlot(), g_model.modelRegistrationID, sizeof(g_model.modelRegistrationID));
grid.nextLine();
// UID
@@ -218,7 +218,7 @@ class RegisterDialog: public Dialog {
rect_t rect = waiting->getRect();
waiting->deleteLater();
rxName = new TextEdit(&content->form, rect, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME);
rxName = new RadioTextEdit(&content->form, rect, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME);
rect = exitButton->getRect();
auto okButton = new TextButton(&content->form, rect, "OK",
[=]() -> int8_t {
@@ -998,7 +998,7 @@ void ModelSetupPage::build(FormWindow * window)
// Model name
new StaticText(window, grid.getLabelSlot(), STR_MODELNAME);
auto text = new TextEdit(window, grid.getFieldSlot(), g_model.header.name, sizeof(g_model.header.name));
auto text = new RadioTextEdit(window, grid.getFieldSlot(), g_model.header.name, sizeof(g_model.header.name));
text->setChangeHandler([=] {
modelslist.load();
auto model = modelslist.getCurrentModel();
@@ -1035,7 +1035,7 @@ void ModelSetupPage::build(FormWindow * window)
// Timer name
new StaticText(window, grid.getLabelSlot(true), STR_NAME);
grid.nextLine();
new TextEdit(group, timerGrid.getSlot(), timer->name, LEN_TIMER_NAME);
new RadioTextEdit(group, timerGrid.getSlot(), timer->name, LEN_TIMER_NAME);
timerGrid.nextLine();
// Timer mode
+1 -1
View File
@@ -324,7 +324,7 @@ class SensorEditWindow : public Page {
// Sensor name
new StaticText(window, grid.getLabelSlot(), STR_NAME);
new TextEdit(window, grid.getFieldSlot(), sensor->label, sizeof(sensor->label));
new RadioTextEdit(window, grid.getFieldSlot(), sensor->label, sizeof(sensor->label));
grid.nextLine();
// Type
+8 -48
View File
@@ -21,6 +21,7 @@
#include "opentx.h"
#include "radio_calibration.h"
#include "sliders.h"
#include "view_main_decoration.h"
#define XPOT_DELTA 10
#define XPOT_DELAY 5 /* cycles */
@@ -81,55 +82,14 @@ void RadioCalibrationPage::buildBody(FormWindow * window)
new StickCalibrationWindow(window, {40, 20, 90, 90}, STICK1, STICK2);
new StickCalibrationWindow(window, {LCD_W - 130, 20, 90, 90}, STICK4, STICK3);
#if defined(PCBHORUS)
new MainViewHorizontalSlider(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return calibratedAnalogs[CALIBRATED_POT1]; });
if (IS_POT_MULTIPOS(POT2)) {
new MainView6POS(this, {LCD_W / 2 - MULTIPOS_W / 2, LCD_H - TRIM_SQUARE_SIZE, MULTIPOS_W + 1, MULTIPOS_H},
[=] { return (1 + (potsPos[1] & 0x0f)); });
}
new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return calibratedAnalogs[CALIBRATED_POT3]; });
#if defined(HARDWARE_EXT1)
coord_t yOffset = TOPBAR_HEIGHT / 2;
if (IS_POT_SLIDER_AVAILABLE(EXT1)) {
new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(true) / 2},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; });
new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(true) / 2},
[=] { return calibratedAnalogs[CALIBRATED_POT_EXT1]; });
}
else {
new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(true)},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; });
}
#endif
#if defined(HARDWARE_EXT2)
if (IS_POT_SLIDER_AVAILABLE(EXT2)) {
new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE,
VERTICAL_SLIDERS_HEIGHT(true) / 2},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; });
new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE,
VERTICAL_SLIDERS_HEIGHT(true) / 2},
[=] { return calibratedAnalogs[CALIBRATED_POT_EXT2]; });
}
else {
new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE,
VERTICAL_SLIDERS_HEIGHT(true)},
[=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; });
}
#endif
#elif defined(PCBNV14)
new MainViewHorizontalSlider(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return calibratedAnalogs[CALIBRATED_POT1]; });
new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE},
[=] { return calibratedAnalogs[CALIBRATED_POT2]; });
rect_t r = { 0, 0, window->width(), window->height() };
auto deco = new ViewMainDecoration(window, r);
deco->setTrimsVisible(false);
deco->setSlidersVisible(true);
deco->setFlightModeVisible(false);
deco->adjustDecoration();
#if defined(PCBNV14)
new TextButton(window, {LCD_W - 120, LCD_H - 140, 90, 40}, "Next",
[=]() -> uint8_t {
nextStep();
@@ -20,6 +20,7 @@
#include "opentx.h"
#include "radio_diaganas.h"
#include "libopenui.h"
constexpr coord_t ANA_OFFSET = 150;
@@ -20,6 +20,7 @@
#include "opentx.h"
#include "radio_diagkeys.h"
#include "libopenui.h"
#if defined(KEYS_GPIO_PIN_PGUP)
constexpr uint8_t KEY_START = 0;
@@ -20,6 +20,7 @@
#include "opentx.h"
#include "radio_ghost_module_config.h"
#include "libopenui.h"
class GhostModuleConfigWindow: public Window
{
+6 -5
View File
@@ -23,6 +23,7 @@
#include "radio_diagkeys.h"
#include "radio_diaganas.h"
#include "opentx.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_GENERAL)
@@ -135,7 +136,7 @@ class BluetoothConfigWindow : public FormGroup
// BT radio name
new StaticText(this, grid.getLabelSlot(true), STR_NAME);
new TextEdit(this, grid.getFieldSlot(), g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME);
new RadioTextEdit(this, grid.getFieldSlot(), g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME);
grid.nextLine();
}
@@ -174,7 +175,7 @@ void RadioHardwarePage::build(FormWindow * window)
grid.nextLine();
for (int i = 0; i < NUM_STICKS; i++) {
new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + 1)));
new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i], LEN_ANA_NAME);
new RadioTextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i], LEN_ANA_NAME);
grid.nextLine();
}
@@ -183,7 +184,7 @@ void RadioHardwarePage::build(FormWindow * window)
grid.nextLine();
for (int i = 0; i < NUM_POTS; i++) {
new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + NUM_STICKS + 1)));
new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME);
new RadioTextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME);
new Choice(window, grid.getFieldSlot(2,1), STR_POTTYPES, POT_NONE, POT_WITHOUT_DETENT,
[=]() -> int {
return bfGet<uint32_t>(g_eeGeneral.potsConfig, 2*i, 2);
@@ -200,7 +201,7 @@ void RadioHardwarePage::build(FormWindow * window)
grid.nextLine();
for (int i = 0; i < NUM_SLIDERS; i++) {
new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + NUM_STICKS + NUM_POTS + 1)));
new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME);
new RadioTextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME);
new Choice(window, grid.getFieldSlot(2,1), STR_SLIDERTYPES, SLIDER_NONE, SLIDER_WITH_DETENT,
[=]() -> int {
uint8_t mask = (0x01 << i);
@@ -220,7 +221,7 @@ void RadioHardwarePage::build(FormWindow * window)
grid.nextLine();
for (int i = 0; i < NUM_SWITCHES; i++) {
new SwitchDynamicLabel(window, grid.getLabelSlot(true), i);
new TextEdit(window, grid.getFieldSlot(2, 0), g_eeGeneral.switchNames[i], LEN_SWITCH_NAME);
new RadioTextEdit(window, grid.getFieldSlot(2, 0), g_eeGeneral.switchNames[i], LEN_SWITCH_NAME);
new Choice(window, grid.getFieldSlot(2, 1), STR_SWTYPES, SWITCH_NONE, SWITCH_TYPE_MAX(i),
[=]() -> int {
return SWITCH_CONFIG(i);
+20 -19
View File
@@ -22,6 +22,7 @@
#include "radio_setup.h"
#include "opentx.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_GENERAL)
@@ -207,16 +208,6 @@ void RadioSetupPage::build(FormWindow * window)
new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.beepVolume));
grid.nextLine();
// Wav volume
new StaticText(window, grid.getLabelSlot(true), STR_WAV_VOLUME);
new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.wavVolume));
grid.nextLine();
// Background volume
new StaticText(window, grid.getLabelSlot(true), STR_BG_VOLUME);
new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.backgroundVolume));
grid.nextLine();
// Beeps length
new StaticText(window, grid.getLabelSlot(true), STR_BEEP_LENGTH);
new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.beepLength));
@@ -227,14 +218,24 @@ void RadioSetupPage::build(FormWindow * window)
auto edit = new NumberEdit(window, grid.getFieldSlot(), 0, 300,
GET_DEFAULT(15 * g_eeGeneral.speakerPitch),
[=](int32_t newValue) {
g_eeGeneral.speakerPitch = newValue / 15;
SET_DIRTY();
g_eeGeneral.speakerPitch = newValue / 15;
SET_DIRTY();
});
edit->setStep(15);
edit->setPrefix("+");
edit->setSuffix("Hz");
grid.nextLine();
// Wav volume
new StaticText(window, grid.getLabelSlot(true), STR_WAV_VOLUME);
new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.wavVolume));
grid.nextLine();
// Background volume
new StaticText(window, grid.getLabelSlot(true), STR_BG_VOLUME);
new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.backgroundVolume));
grid.nextLine();
#if defined(VARIO)
{
new Subtitle(window, grid.getLabelSlot(), STR_VARIO);
@@ -304,6 +305,12 @@ void RadioSetupPage::build(FormWindow * window)
edit->setSuffix("v");
grid.nextLine();
// Inactivity alarm
new StaticText(window, grid.getLabelSlot(true), STR_INACTIVITYALARM);
edit = new NumberEdit(window, grid.getFieldSlot(), 0, 250, GET_SET_DEFAULT(g_eeGeneral.inactivityTimer));
edit->setSuffix("minutes");
grid.nextLine();
// Alarms warning
new StaticText(window, grid.getLabelSlot(true), STR_ALARMWARNING);
new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.disableAlarmWarning));
@@ -313,12 +320,6 @@ void RadioSetupPage::build(FormWindow * window)
new StaticText(window, grid.getLabelSlot(true), STR_RSSI_SHUTDOWN_ALARM);
new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.disableRssiPoweroffAlarm));
grid.nextLine();
// Inactivity alarm
new StaticText(window, grid.getLabelSlot(true), STR_INACTIVITYALARM);
edit = new NumberEdit(window, grid.getFieldSlot(), 0, 250, GET_SET_DEFAULT(g_eeGeneral.inactivityTimer));
edit->setSuffix("minutes");
grid.nextLine();
}
// Backlight
@@ -399,7 +400,7 @@ void RadioSetupPage::build(FormWindow * window)
#if defined(PXX2)
// Owner ID
new StaticText(window, grid.getLabelSlot(), STR_OWNER_ID);
new TextEdit(window, grid.getFieldSlot(), g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID);
new RadioTextEdit(window, grid.getFieldSlot(), g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID);
grid.nextLine();
#endif
@@ -20,6 +20,7 @@
#include "radio_spectrum_analyser.h"
#include "opentx.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_GENERAL)
+1
View File
@@ -20,6 +20,7 @@
#include "radio_trainer.h"
#include "opentx.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_GENERAL)
+304 -201
View File
@@ -23,75 +23,20 @@
#include "opentx.h"
#include "view_main.h"
#include "widget_settings.h"
#include "topbar.h"
#include "libopenui.h"
#include "layouts/layout_factory_impl.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
ScreenUserInterfacePage::ScreenUserInterfacePage():
PageTab(STR_USER_INTERFACE, ICON_THEME_SETUP)
class LayoutChoice: public FormField
{
}
void ScreenUserInterfacePage::build(FormWindow * window)
{
FormGridLayout grid;
// Theme choice
new StaticText(window, grid.getLabelSlot(), STR_THEME, 0, 0);
// TODO: Theme picklist
grid.nextLine();
// Theme options ?
// Top Bar
new StaticText(window, grid.getLabelSlot(), STR_TOP_BAR, 0, 0);
// TODO: enable settings topbar widgets
grid.nextLine();
}
ScreenAddPage::ScreenAddPage(ScreenMenu * menu, uint8_t pageIndex):
PageTab(),
menu(menu),
pageIndex(pageIndex)
{
setTitle(STR_ADD_MAIN_VIEW);
setIcon(ICON_THEME_ADD_VIEW);
}
void ScreenAddPage::build(FormWindow * window)
{
rect_t buttonRect = {LCD_W / 2 - 100, (window->height() - 24) / 2, 200, 24};
auto button = new TextButton(window, buttonRect, STR_ADD_MAIN_VIEW);
button->setPressHandler([=]() -> uint8_t {
auto& screen = customScreens[pageIndex];
auto& screenData = g_model.screenData[pageIndex];
const LayoutFactory * factory = getRegisteredLayouts().front();
screen = factory->create(&screenData.layoutData);
strncpy(screenData.LayoutId, factory->getId(), sizeof(screenData.LayoutId));
auto tab = new ScreenSetupPage(menu, screen, screenData);
std::string title(STR_MAIN_VIEW_X);
title.back() = pageIndex + '0';
tab->setTitle(title);
tab->setIcon(ICON_THEME_VIEW1 + pageIndex);
menu->removeTab(pageIndex);
menu->addTab(tab);
menu->setCurrentTab(pageIndex);
if (pageIndex < MAX_CUSTOM_SCREENS - 1) {
menu->addTab(new ScreenAddPage(menu, pageIndex + 1));
}
storageDirty(EE_MODEL);
return 0;
});
}
class LayoutChoice: public FormField {
public:
LayoutChoice(Window * parent, const rect_t & rect, std::function<const LayoutFactory *()> getValue, std::function<void(const LayoutFactory *)> setValue):
typedef std::function<const LayoutFactory *()> LayoutFactoryGetter;
typedef std::function<void(const LayoutFactory *)> LayoutFactorySetter;
LayoutChoice(Window * parent, const rect_t & rect, LayoutFactoryGetter getValue, LayoutFactorySetter setValue):
FormField(parent, {rect.x, rect.y, 59, 33}),
getValue(std::move(getValue)),
setValue(std::move(setValue))
@@ -102,7 +47,9 @@ class LayoutChoice: public FormField {
{
FormField::paint(dc);
auto layout = getValue();
layout->drawThumb(dc, 4, 4, editMode ? FOCUS_COLOR : LINE_COLOR);
if (layout) {
layout->drawThumb(dc, 4, 4, editMode ? FOCUS_COLOR : LINE_COLOR);
}
}
#if defined(HARDWARE_KEYS)
@@ -162,94 +109,83 @@ class LayoutChoice: public FormField {
std::function<void(const LayoutFactory *)> setValue;
};
ScreenSetupPage::ScreenSetupPage(ScreenMenu * menu, Layout*& screen, CustomScreenData& screenData):
PageTab(),
menu(menu),
screen(screen),
screenData(screenData)
SetupWidgetsPageSlot::SetupWidgetsPageSlot(FormGroup * parent, const rect_t & rect, WidgetsContainer* container, uint8_t slotIndex):
Button(parent, rect)
{
setPressHandler([parent, container, slotIndex]() -> uint8_t {
Menu * menu = new Menu(parent);
menu->addLine(TR_SELECT_WIDGET, [=]() {
Menu * menu = new Menu(parent);
for (auto factory: getRegisteredWidgets()) {
menu->addLine(factory->getName(), [=]() {
container->createWidget(slotIndex, factory);
});
}
});
if (container->getWidget(slotIndex)) {
menu->addLine(TR_WIDGET_SETTINGS, [=]() {
auto widget = container->getWidget(slotIndex);
new WidgetSettings(parent, widget);
});
menu->addLine(STR_REMOVE_WIDGET, [=]() {
container->removeWidget(slotIndex);
});
}
return 0;
});
}
class SetupWidgetsPageSlot: public Button
void SetupWidgetsPageSlot::paint(BitmapBuffer * dc)
{
public:
SetupWidgetsPageSlot(FormGroup * parent, const rect_t & rect, Layout* screen, uint8_t slotIndex):
Button(parent, rect),
screen(screen),
slotIndex(slotIndex)
{
widget = screen->getWidget(slotIndex);
setPressHandler([=]() -> uint8_t {
if (widget) {
Menu * menu = new Menu(this);
menu->addLine(TR_SELECT_WIDGET, [=]() {
Menu * menu = new Menu(this);
for (auto factory: getRegisteredWidgets()) {
menu->addLine(factory->getName(), [=]() {
if (widget) widget->deleteLater();
widget = screen->createWidget(slotIndex, factory);
});
}
});
menu->addLine(TR_WIDGET_SETTINGS, [=]() {
new WidgetSettings(this, widget);
});
menu->addLine(STR_REMOVE_WIDGET, [=]() {
widget->deleteLater();
widget = nullptr;
});
return 0;
}
else {
Menu * menu = new Menu(this);
for (auto factory: getRegisteredWidgets()) {
menu->addLine(factory->getName(), [=]() {
if (widget) widget->deleteLater();
widget = screen->createWidget(slotIndex, factory);
});
}
}
return 0;
});
}
void paint(BitmapBuffer * dc) override
{
if (hasFocus()) {
dc->drawSolidRect(0, 0, width(), height(), 1, DEFAULT_COLOR);
}
else {
dc->drawRect(0, 0, width(), height(), 1, DOTTED, DEFAULT_COLOR);
}
}
protected:
Layout * screen;
uint8_t slotIndex;
Widget * widget = nullptr;
};
if (hasFocus()) {
dc->drawRect(0, 0, width()-1, height()-1, 2, STASHED, CHECKBOX_COLOR);
}
else {
dc->drawSolidRect(0, 0, width()-1, height()-1, 2, LINE_COLOR);
}
}
class SetupWidgetsPage: public FormWindow
{
public:
SetupWidgetsPage(Layout * screen):
FormWindow(MainWindow::instance(), {0, 0, LCD_W, LCD_H}, OPAQUE | FORM_FORWARD_FOCUS),
screen(screen),
oldParent(screen->getParent())
SetupWidgetsPage(ScreenMenu* menu, uint8_t customScreenIdx):
FormWindow(ViewMain::instance(), {0, 0, 0, 0}, FORM_FORWARD_FOCUS),
menu(menu),
customScreenIdx(customScreenIdx)
{
Layer::push(this);
clearFocus();
// attach this custom screen here so we can display it
screen->attach(this);
// attach this custom screen here so we can display it
auto screen = customScreens[customScreenIdx];
if (screen) {
screen->attach(this);
setRect(screen->getRect());
screen->setLeft(0);
auto viewMain = ViewMain::instance();
savedView = viewMain->getCurrentMainView();
viewMain->setCurrentMainView(customScreenIdx);
viewMain->bringToTop();
setLeft(viewMain->getMainViewLeftPos(customScreenIdx));
}
for (unsigned i = 0; i < screen->getZonesCount(); i++) {
auto rect = screen->getZone(i);
new SetupWidgetsPageSlot(this, {rect.x - 1, rect.y - 1, rect.w + 2, rect.h + 2}, screen, i);
auto widget = new SetupWidgetsPageSlot(this, rect, customScreens[customScreenIdx], i);
if (i == 0) widget->setFocus();
}
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "SetupWidgetPage(idx=" + std::to_string(customScreenIdx) + ")";
}
#endif
void deleteLater(bool detach = true, bool trash = true) override
{
Layer::pop(this);
@@ -257,14 +193,27 @@ class SetupWidgetsPage: public FormWindow
#if defined(HARDWARE_TOUCH)
Keyboard::hide();
#endif
if (oldParent) {
// give it back to its old parent before it gets deleted
screen->attach(oldParent);
auto screen = customScreens[customScreenIdx];
if (screen) {
auto viewMain = ViewMain::instance();
screen->attach(viewMain);
screen->setRect(getRect());
viewMain->setCurrentMainView(savedView);
}
FormWindow::deleteLater(detach, trash);
// restore screen setting tab on top
menu->bringToTop();
}
#if defined(HARDWARE_TOUCH)
bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override
{
// prevent screen sliding when setting up widgets
return true;
}
#endif
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override
{
@@ -273,22 +222,169 @@ class SetupWidgetsPage: public FormWindow
if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) {
killEvents(event);
deleteLater();
SET_DIRTY();
}
}
#endif
protected:
Layout* screen;
Window* oldParent;
void paint(BitmapBuffer * dc) override
{
dc->clear(DEFAULT_BGCOLOR);
}
ScreenMenu* menu;
uint8_t customScreenIdx;
unsigned savedView = 0;
// void paint(BitmapBuffer * dc) override
// {
// //ViewMain::instance()->fullPaint(dc);
// //dc->clear(DEFAULT_BGCOLOR);
// }
};
ScreenUserInterfacePage::ScreenUserInterfacePage(ScreenMenu* menu):
PageTab(STR_USER_INTERFACE, ICON_THEME_SETUP),
menu(menu)
{
}
void ScreenUserInterfacePage::build(FormWindow * window)
{
FormGridLayout grid;
// Theme choice
new StaticText(window, grid.getLabelSlot(), STR_THEME, 0, 0);
// TODO: Theme picklist
grid.nextLine();
// Theme options ?
// Top Bar
new StaticText(window, grid.getLabelSlot(), STR_TOP_BAR, 0, 0);
auto menu = this->menu;
auto setupTopbarWidgets = new TextButton(window, grid.getFieldSlot(), STR_SETUP_WIDGETS);
setupTopbarWidgets->setPressHandler([menu]() -> uint8_t {
new SetupTopBarWidgetsPage(menu);
return 0;
});
}
ScreenAddPage::ScreenAddPage(ScreenMenu * menu, uint8_t pageIndex):
PageTab(),
menu(menu),
pageIndex(pageIndex)
{
setTitle(STR_ADD_MAIN_VIEW);
setIcon(ICON_THEME_ADD_VIEW);
}
extern const LayoutFactory * defaultLayout;
void ScreenAddPage::build(FormWindow * window)
{
rect_t buttonRect = {LCD_W / 2 - 100, (window->height() - 24) / 2, 200, 24};
auto button = new TextButton(window, buttonRect, STR_ADD_MAIN_VIEW);
auto pageIndex = this->pageIndex;
auto menu = this->menu;
button->setPressHandler([menu, pageIndex]() -> uint8_t {
// First page is "User interface", subtract it
auto newIdx = pageIndex - 1;
TRACE("ScreenAddPage: add screen: newIdx = %d", newIdx);
auto& screen = customScreens[newIdx];
auto& screenData = g_model.screenData[newIdx];
TRACE("ScreenAddPage: add screen: screen = %p", screen);
const LayoutFactory * factory = defaultLayout;
if (factory) {
TRACE("ScreenAddPage: add screen: factory = %p", factory);
screen = factory->create(&screenData.layoutData);
strncpy(screenData.LayoutId, factory->getId(), sizeof(screenData.LayoutId));
TRACE("ScreenAddPage: add screen: LayoutId = %s", screenData.LayoutId);
auto tab = new ScreenSetupPage(menu, pageIndex, newIdx);
std::string title(STR_MAIN_VIEW_X);
title.back() = newIdx + '1';
tab->setTitle(title);
tab->setIcon(ICON_THEME_VIEW1 + newIdx);
auto viewMain = ViewMain::instance();
viewMain->setMainViewsCount(viewMain->getMainViewsCount() + 1);
// remove current tab first
menu->setCurrentTab(0);
menu->removeTab(pageIndex);
// add the new one
menu->addTab(tab);
menu->setCurrentTab(pageIndex);
if (menu->getTabs() < MAX_CUSTOM_SCREENS) {
menu->addTab(new ScreenAddPage(menu, menu->getTabs()));
}
}
else {
TRACE("Add main view: factory is NULL");
}
return 0;
});
}
ScreenSetupPage::ScreenSetupPage(ScreenMenu * menu, unsigned pageIndex, unsigned customScreenIndex):
PageTab(),
menu(menu),
pageIndex(pageIndex),
customScreenIndex(customScreenIndex)
{
}
static void updateLayoutOptions(FormGroup* optionsWindow, unsigned customScreenIndex) // TODO
{
FormGridLayout grid;
optionsWindow->clear();
// Layout options...
int index = 0;
auto layout = dynamic_cast<Layout*>(customScreens[customScreenIndex]);
if (!layout) {
return;
}
auto factory = layout->getFactory();
if (!factory) {
return;
}
for (auto * option = factory->getOptions(); option->name; option++, index++) {
auto layoutData = &g_model.screenData[customScreenIndex].layoutData;
ZoneOptionValue * value = &layoutData->options[index].value;
// Option label
new StaticText(optionsWindow, grid.getLabelSlot(false), option->name);
// Option value
switch (option->type) {
case ZoneOption::Bool:
new CheckBox(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->boolValue));
break;
case ZoneOption::Color:
new ColorEdit(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->unsignedValue));
break;
default:
break;
}
grid.nextLine();
}
optionsWindow->adjustHeight();
}
void ScreenSetupPage::build(FormWindow * window)
{
FormGridLayout grid;
@@ -296,75 +392,82 @@ void ScreenSetupPage::build(FormWindow * window)
// Layout choice...
new StaticText(window, grid.getLabelSlot(false), STR_LAYOUT);
auto layoutSlot = grid.getFieldSlot();
layoutSlot.h = 2 * PAGE_LINE_HEIGHT - 1;
layoutChoice = new LayoutChoice(window, layoutSlot, GET_VALUE(screen->getFactory()), [=](const LayoutFactory *factory) {
delete screen;
screen = factory->create(&screenData.layoutData);
strncpy(screenData.LayoutId, factory->getId(), sizeof(CustomScreenData::LayoutId));
SET_DIRTY();
updateLayoutOptions();
});
// Dynamic options window...
auto idx = customScreenIndex;
rect_t optRect = {0, grid.getWindowHeight(), window->width(), 0};
auto optionsWindow = new FormGroup(window, optRect, FORWARD_SCROLL | FORM_FORWARD_FOCUS);
LayoutChoice::LayoutFactoryGetter getFactory = [idx] () -> const LayoutFactory * {
auto layout = dynamic_cast<Layout*>(customScreens[idx]);
if (!layout) return nullptr;
return layout->getFactory();
};
LayoutChoice::LayoutFactorySetter setLayout = [idx, optionsWindow](const LayoutFactory *factory) {
// delete any options potentially accessing
// the old custom screen
optionsWindow->clear();
createCustomScreen(factory, idx);
updateLayoutOptions(optionsWindow, idx);
};
auto layoutChoice = new LayoutChoice(window, layoutSlot, getFactory, setLayout);
grid.nextLine(layoutChoice->height());
// Setup widgets button...
setupWidgetsButton = new TextButton(window, grid.getFieldSlot(), STR_SETUP_WIDGETS);
setupWidgetsButton->setPressHandler([=]() -> uint8_t {
new SetupWidgetsPage(screen);
auto setupWidgetsButton = new TextButton(window, grid.getFieldSlot(), STR_SETUP_WIDGETS);
auto menu = this->menu;
setupWidgetsButton->setPressHandler([idx, menu]() -> uint8_t {
new SetupWidgetsPage(menu, idx);
return 0;
});
grid.nextLine();
// Dynamic options window...
optionsWindow = new FormGroup(window, {0, grid.getWindowHeight(), LCD_W, 0}, FORWARD_SCROLL | FORM_FORWARD_FOCUS);
// place the options window at the right spot
optionsWindow->setTop(grid.getWindowHeight());
updateLayoutOptions(optionsWindow, idx);
grid.addWindow(optionsWindow);
updateLayoutOptions();
}
void ScreenSetupPage::updateLayoutOptions()
{
FormGridLayout grid;
optionsWindow->clear();
// fix focus order due to early insertion
FormField::link(setupWidgetsButton, optionsWindow);
window->setLastField(optionsWindow);
// Prevent removing the last page
if (customScreens[1] != nullptr) {
auto button = new TextButton(window, grid.getFieldSlot(), STR_REMOVE_SCREEN);
auto menu = this->menu;
button->setPressHandler([menu, idx]() -> uint8_t {
// Layout options...
int index = 0;
for (auto * option = screen->getFactory()->getOptions(); option->name; option++, index++) {
ZoneOptionValue * value = screen->getOptionValue(index);
// Set the current tab to "User interface" to trigger body clearing
menu->setCurrentTab(0);
// Option label
new StaticText(optionsWindow, grid.getLabelSlot(false), option->name);
// Remove this screen from the model
disposeCustomScreen(idx);
// Option value
switch (option->type) {
case ZoneOption::Bool:
new CheckBox(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->boolValue));
break;
// Delete all custom screens
deleteCustomScreens();
case ZoneOption::Color:
new ColorEdit(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->unsignedValue));
break;
// ... and reload
loadCustomScreens();
menu->updateTabs();
default:
break;
}
grid.nextLine();
}
// Let's try to stay on the same page
// (first tab is "User interface")
auto pageIdx = idx + 1;
//if (pageIndex > 0 || customScreens[1]) {
auto button = new TextButton(optionsWindow, grid.getFieldSlot(), STR_REMOVE_SCREEN);
button->setPressHandler([=]() -> uint8_t {
// if (pageIndex < MAX_CUSTOM_SCREENS - 1) {
// memmove(&g_model.screenData[pageIndex], &g_model.screenData[pageIndex + 1], sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - pageIndex - 1));
// }
// memset(&g_model.screenData[MAX_CUSTOM_SCREENS - 1], 0, sizeof(CustomScreenData));
memset(&screenData, 0, sizeof(CustomScreenData));
loadCustomScreens();
menu->updateTabs();
storageDirty(EE_MODEL);
return 0;
// Subtract one more as the last one is "New main screen"
if (pageIdx > menu->getTabs() - 2) {
pageIdx = menu->getTabs() - 2;
}
menu->setCurrentTab(pageIdx);
return 0;
});
//}
optionsWindow->adjustHeight();
optionsWindow->getParent()->adjustInnerHeight();
// fix focus order due to early insertion
FormField::link(button, layoutChoice);
}
}
+12 -8
View File
@@ -24,11 +24,12 @@
class ScreenUserInterfacePage: public PageTab {
public:
ScreenUserInterfacePage();
ScreenUserInterfacePage(ScreenMenu* menu);
void build(FormWindow * window) override;
protected:
ScreenMenu * menu;
};
class ScreenAddPage: public PageTab {
@@ -44,17 +45,20 @@ class ScreenAddPage: public PageTab {
class ScreenSetupPage: public PageTab {
public:
ScreenSetupPage(ScreenMenu * menu, Layout*& screen, CustomScreenData& screenData);
ScreenSetupPage(ScreenMenu * menu, unsigned pageIndex, unsigned customScreenIndex);
void build(FormWindow * window) override;
protected:
ScreenMenu * menu;
Layout *& screen;
CustomScreenData& screenData;
FormGroup * optionsWindow = nullptr;
TextButton * setupWidgetsButton = nullptr;
FormField * layoutChoice = nullptr;
unsigned pageIndex;
unsigned customScreenIndex;
};
void updateLayoutOptions();
class SetupWidgetsPageSlot: public Button
{
public:
SetupWidgetsPageSlot(FormGroup * parent, const rect_t & rect, WidgetsContainer* container, uint8_t slotIndex);
void paint(BitmapBuffer * dc) override;
};
@@ -1,690 +0,0 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "opentx.h"
uint8_t THEME_ICONS[] = {
ICON_THEME,
ICON_THEME_SETUP,
ICON_THEME_VIEW1,
ICON_THEME_VIEW2,
ICON_THEME_VIEW3,
ICON_THEME_VIEW4,
ICON_THEME_VIEW5,
ICON_THEME_ADD_VIEW,
};
Layout * currentScreen;
WidgetsContainerInterface * currentContainer;
Widget * currentWidget;
uint8_t currentZone;
bool widgetNeedsSettings;
#define SCREENS_SETUP_2ND_COLUMN 200
char fileSelection[LEN_ZONE_OPTION_STRING];
uint8_t fileSelectionDone;
int updateMainviewsMenu();
bool menuScreenAdd(event_t event);
void onScreenSetupMenu(const char * result);
void onZoneOptionFileSelectionMenu(const char * result)
{
if (result == STR_UPDATE_LIST) {
if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, LEN_ZONE_OPTION_STRING, nullptr)) {
POPUP_WARNING(STR_NO_BITMAPS_ON_SD);
}
}
else if (result != STR_EXIT) {
fileSelectionDone = true;
memcpy(fileSelection, result, sizeof(fileSelection));
}
}
uint8_t getZoneOptionColumns(const ZoneOption * option)
{
if (option->type == ZoneOption::Color) {
return uint8_t(2 | NAVIGATION_LINE_BY_LINE);
}
else {
return 0;
}
}
uint8_t editColorPart(coord_t x, coord_t y, event_t event, uint8_t part, uint8_t value, LcdFlags attr, uint32_t i_flags)
{
const char * STR_COLOR_PARTS = "\002" "R:" "G:" "B:";
uint8_t PART_BITS[] = { 5, 6, 5 };
lcdDrawTextAtIndex(x, y, STR_COLOR_PARTS, part, (attr && menuHorizontalPosition < 0) ? FOCUS_COLOR : DEFAULT_COLOR);
lcdDrawNumber(x + 20, y, value << (8-PART_BITS[part]), LEFT|DEFAULT_COLOR|((attr && (menuHorizontalPosition < 0 || menuHorizontalPosition == part)) ? attr : DEFAULT_COLOR));
if (attr && menuHorizontalPosition == part) {
value = checkIncDec(event, value, 0, (1 << PART_BITS[part])-1, i_flags);
}
return value;
}
bool editZoneOption(coord_t y, const ZoneOption * option, ZoneOptionValue * value, LcdFlags attr, uint32_t i_flags, event_t event)
{
lcdDrawText(MENUS_MARGIN_LEFT, y, option->name);
if (option->type == ZoneOption::Bool) {
value->boolValue = editCheckBox(value->boolValue, SCREENS_SETUP_2ND_COLUMN, y, attr, event); // TODO always does storageDirty(EE_MODEL)
}
else if (option->type == ZoneOption::Integer) {
lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN, y, value->signedValue, attr | LEFT);
if (attr) {
CHECK_INCDEC_MODELVAR(event, value->signedValue, option->min.signedValue, option->max.signedValue); // TODO i_flags
}
}
else if (option->type == ZoneOption::String) {
editName(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), event, attr); // TODO i_flags?
}
else if (option->type == ZoneOption::File) {
if (ZEXIST(value->stringValue))
lcdDrawSizedText(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), attr);
else
lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); // TODO define
if (attr) {
if (event==EVT_KEY_FIRST(KEY_ENTER)) {
s_editMode = 0;
if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(value->stringValue), value->stringValue, LIST_NONE_SD_FILE)) {
fileSelectionDone = false;
POPUP_MENU_START(onZoneOptionFileSelectionMenu);
}
else {
POPUP_WARNING(STR_NO_BITMAPS_ON_SD);
}
}
else if (fileSelectionDone) {
memcpy(value->stringValue, fileSelection, sizeof(fileSelection));
fileSelectionDone = false;
storageDirty(i_flags);
}
}
}
else if (option->type == ZoneOption::TextSize) {
lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_TEXT_SIZE, value->unsignedValue, attr);
if (attr) {
value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, 4, i_flags);
}
}
else if (option->type == ZoneOption::Timer) {
drawStringWithIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_TIMER, value->unsignedValue + 1, attr);
if (attr) {
value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, MAX_TIMERS - 1, i_flags);
}
}
else if (option->type == ZoneOption::Source) {
drawSource(SCREENS_SETUP_2ND_COLUMN, y, value->unsignedValue, attr);
if (attr) {
CHECK_INCDEC_MODELSOURCE(event, value->unsignedValue, 1, MIXSRC_LAST_TELEM);
}
}
else if (option->type == ZoneOption::Color) {
RGB_SPLIT(value->unsignedValue, r, g, b);
if (attr && menuHorizontalPosition < 0) {
lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-3, y-1, 230, FH+1, FOCUS_BGCOLOR);
}
lcdSetColor(value->unsignedValue);
lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-1, y+1, 42, 17, DEFAULT_COLOR);
lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN, y+2, 40, 15, CUSTOM_COLOR);
r = editColorPart(SCREENS_SETUP_2ND_COLUMN + 50, y, event, 0, r, attr, i_flags);
g = editColorPart(SCREENS_SETUP_2ND_COLUMN + 110, y, event, 1, g, attr, i_flags);
b = editColorPart(SCREENS_SETUP_2ND_COLUMN + 170, y, event, 2, b, attr, i_flags);
if (attr && checkIncDec_Ret) {
value->unsignedValue = RGB_JOIN(r, g, b);
}
}
return (attr && checkIncDec_Ret);
}
int getOptionsCount(const ZoneOption * options)
{
if (options == nullptr) {
return 0;
}
else {
int count = 0;
for (const ZoneOption * option = options; option->name; option++) {
count++;
}
return count;
}
}
template <class T>
bool menuSettings(const char * title, T * object, uint32_t i_flags, event_t event)
{
if (object->getErrorMessage()) {
// display error instead of widget settings
// TODO nicer display (proper word-wrap)
SIMPLE_SUBMENU("Widget Error", ICON_MODEL_LUA_SCRIPTS, 1);
int len = strlen(object->getErrorMessage());
int y = 3*FH;
const char * p = object->getErrorMessage();
while (len > 0) {
lcdDrawSizedText(MENUS_MARGIN_LEFT, y, p, 30);
p += 30;
y += FH;
len -= 30;
}
return true;
}
const ZoneOption * options = object->getOptions();
linesCount = getOptionsCount(options);
uint8_t mstate_tab[MAX_WIDGET_OPTIONS];
for (int i=0; i<linesCount; i++) {
mstate_tab[i] = getZoneOptionColumns(&options[i]);
}
CUSTOM_SUBMENU_WITH_OPTIONS(title, ICON_THEME, linesCount, OPTION_MENU_TITLE_BAR);
for (int i=0; i<NUM_BODY_LINES+1; i++) {
coord_t y = MENU_CONTENT_TOP + i * FH;
int k = i + menuVerticalOffset;
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
LcdFlags attr = (menuVerticalPosition == k ? blink : 0);
if (k < linesCount) {
const ZoneOption * option = &options[k];
ZoneOptionValue * value = object->getOptionValue(k);
if (editZoneOption(y, option, value, attr, i_flags, event)) {
object->update();
}
}
}
return true;
}
bool menuWidgetSettings(event_t event)
{
return menuSettings<Widget>(STR_WIDGET_SETTINGS, currentWidget, EE_MODEL, event);
}
bool menuWidgetChoice(event_t event)
{
static Widget * previousWidget = nullptr;
static Widget * currentWidget = nullptr;
static std::list<const WidgetFactory *>::const_iterator iterator;
static Widget::PersistentData tempData;
switch (event) {
case EVT_ENTRY:
{
previousWidget = currentContainer->getWidget(currentZone);
currentContainer->setWidget(currentZone, nullptr);
iterator = getRegisteredWidgets().cbegin();
if (previousWidget) {
const WidgetFactory * factory = previousWidget->getFactory();
std::list<const WidgetFactory *>::const_iterator it = getRegisteredWidgets().cbegin();
for (; it != getRegisteredWidgets().cend(); ++it) {
if (factory->getName() == (*it)->getName()) {
iterator = it;
break;
}
}
}
if (iterator != getRegisteredWidgets().cend())
currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData);
break;
}
case EVT_KEY_FIRST(KEY_EXIT):
if (previousWidget) {
if (currentWidget)
delete currentWidget;
currentContainer->setWidget(currentZone, previousWidget);
}
popMenu();
return false;
case EVT_KEY_FIRST(KEY_ENTER):
if (iterator != getRegisteredWidgets().cend()) {
if (previousWidget)
delete previousWidget;
currentContainer->createWidget(currentZone, *iterator);
widgetNeedsSettings = currentContainer->getWidget(currentZone)->getFactory()->getOptions();
storageDirty(EE_MODEL);
}
popMenu();
return false;
case EVT_ROTARY_RIGHT:
if (iterator != getRegisteredWidgets().cend() && iterator != --getRegisteredWidgets().cend()) {
++iterator;
if (currentWidget)
delete currentWidget;
currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData);
}
break;
case EVT_ROTARY_LEFT:
if (iterator != getRegisteredWidgets().cbegin()) {
--iterator;
if (currentWidget)
delete currentWidget;
currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData);
}
break;
}
currentScreen->refresh();
Zone zone = currentContainer->getZone(currentZone);
lcdDrawFilledRect(0, 0, zone.x-2, LCD_H, SOLID, OVERLAY_COLOR | OPACITY(8));
lcdDrawFilledRect(zone.x+zone.w+2, 0, LCD_W-zone.x-zone.w-2, LCD_H, SOLID, OVERLAY_COLOR | OPACITY(8));
lcdDrawFilledRect(zone.x-2, 0, zone.w+4, zone.y-2, SOLID, OVERLAY_COLOR | OPACITY(8));
lcdDrawFilledRect(zone.x-2, zone.y+zone.h+2, zone.w+4, LCD_H-zone.y-zone.h-2, SOLID, OVERLAY_COLOR | OPACITY(8));
if (currentWidget)
currentWidget->refresh();
if (iterator != getRegisteredWidgets().cbegin()) {
lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, FOCUS_BGCOLOR);
lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_LEFT, FOCUS_COLOR);
}
if (iterator != --getRegisteredWidgets().cend()) {
lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, FOCUS_BGCOLOR);
lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_RIGHT, FOCUS_COLOR);
}
if (currentWidget)
lcdDrawText(zone.x + zone.w, zone.y-1, currentWidget->getFactory()->getName(), RIGHT | DEFAULT_COLOR | FONT(XS) | INVERS);
return true;
}
void onZoneMenu(const char * result)
{
if (result == STR_SELECT_WIDGET) {
pushMenu(menuWidgetChoice);
}
else if (result == STR_WIDGET_SETTINGS) {
pushMenu(menuWidgetSettings);
}
else if (result == STR_REMOVE_WIDGET) {
currentContainer->createWidget(currentZone, nullptr);
storageDirty(EE_MODEL);
}
}
bool menuWidgetsSetup(event_t event)
{
switch (event) {
case EVT_ENTRY:
menuVerticalPosition = 0;
break;
case EVT_KEY_FIRST(KEY_EXIT):
killEvents(KEY_EXIT);
popMenu();
return false;
}
currentScreen->refresh();
for (int i = currentContainer->getZonesCount() - 1; i >= 0; i--) {
Zone zone = currentContainer->getZone(i);
LcdFlags color;
int padding, thickness;
if (currentContainer == topbar) {
color = MENU_COLOR;
padding = 2;
thickness = 1;
}
else {
color = FOCUS_BGCOLOR;
padding = 4;
thickness = 2;
}
if (menuVerticalPosition == i) {
lcdDrawSolidRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, color);
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
killEvents(KEY_ENTER);
currentZone = menuVerticalPosition;
currentWidget = currentContainer->getWidget(menuVerticalPosition);
if (currentWidget) {
POPUP_MENU_ADD_ITEM(STR_SELECT_WIDGET);
if (currentWidget->getFactory()->getOptions())
POPUP_MENU_ADD_ITEM(STR_WIDGET_SETTINGS);
POPUP_MENU_ADD_ITEM(STR_REMOVE_WIDGET);
POPUP_MENU_START(onZoneMenu);
}
else {
onZoneMenu(STR_SELECT_WIDGET);
}
}
}
else {
if (widgetNeedsSettings) {
currentWidget = currentContainer->getWidget(menuVerticalPosition);
if (currentWidget) {
widgetNeedsSettings = false;
onZoneMenu(STR_WIDGET_SETTINGS);
}
}
lcdDrawRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, 0x3F, color);
}
}
navigate(event, currentContainer->getZonesCount(), currentContainer->getZonesCount(), 1);
return true;
}
template <class T>
T * editThemeChoice(coord_t x, coord_t y, std::list<T *> & elList, T * current, bool needsOffsetCheck, LcdFlags attr, event_t event)
{
static uint8_t menuHorizontalOffset = 0;
uint8_t elCount = elList.size(), last, idx;
coord_t pos;
int currentIndex = 0;
typename std::list<T *>::const_iterator elItr = elList.cbegin();
if (!elCount)
return nullptr;
for (; elItr != elList.cend(); ++elItr, ++currentIndex) {
if ((*elItr) == current)
break;
}
if (event == EVT_ENTRY) {
menuHorizontalOffset = 0;
needsOffsetCheck = true;
}
if (needsOffsetCheck) {
if (currentIndex < menuHorizontalOffset) {
menuHorizontalOffset = currentIndex;
}
else if (currentIndex > menuHorizontalOffset + 3) {
menuHorizontalOffset = currentIndex - 3;
}
}
if (attr) {
if (menuHorizontalPosition < 0) {
lcdDrawSolidFilledRect(x-3, y-1, min<uint8_t>(4, elCount)*56+1, 2*FH-5, FOCUS_BGCOLOR);
}
else {
if (needsOffsetCheck) {
menuHorizontalPosition = currentIndex;
}
else if (menuHorizontalPosition < menuHorizontalOffset) {
menuHorizontalOffset = menuHorizontalPosition;
}
else if (menuHorizontalPosition > menuHorizontalOffset + 3) {
menuHorizontalOffset = menuHorizontalPosition - 3;
}
}
}
last = min<uint8_t>(menuHorizontalOffset + 4, elCount);
idx = menuHorizontalOffset;
pos = x;
elItr = elList.cbegin();
std::advance(elItr, min<uint8_t>(menuHorizontalOffset, elCount - 1));
for (; idx < last && elItr != elList.cend(); ++idx, ++elItr, pos += 56) {
(*elItr)->drawThumb(pos, y+1, current == (*elItr) ? ((attr && menuHorizontalPosition < 0) ? FOCUS_COLOR : FOCUS_BGCOLOR) : LINE_COLOR);
}
if (elCount > 4) {
lcdDrawBitmapPattern(x - 12, y+1, LBM_CARROUSSEL_LEFT, menuHorizontalOffset > 0 ? LINE_COLOR : DISABLE_COLOR);
lcdDrawBitmapPattern(x + 4 * 56, y+1, LBM_CARROUSSEL_RIGHT, last < getRegisteredLayouts().size() ? LINE_COLOR : DISABLE_COLOR);
}
if (attr && menuHorizontalPosition >= 0) {
lcdDrawSolidRect(x + (menuHorizontalPosition - menuHorizontalOffset) * 56 - 3, y - 1, 57, 35, 1, FOCUS_BGCOLOR);
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
s_editMode = 0;
elItr = elList.cbegin();
if (menuHorizontalPosition < (int)elList.size())
std::advance(elItr, menuHorizontalPosition);
if (elItr != elList.cend())
return (*elItr);
else
return nullptr;
}
}
return nullptr;
}
enum menuScreensThemeItems {
ITEM_SCREEN_SETUP_THEME,
ITEM_SCREEN_SETUP_THEME_OPTION1 = ITEM_SCREEN_SETUP_THEME+2
};
bool menuScreensTheme(event_t event)
{
bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0);
const ZoneOption * options = theme->getOptions();
int optionsCount = getOptionsCount(options);
linesCount = ITEM_SCREEN_SETUP_THEME_OPTION1 + optionsCount + 1;
menuPageCount = updateMainviewsMenu();
uint8_t mstate_tab[2 + MAX_THEME_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE | uint8_t(getRegisteredThemes().size()-1)), ORPHAN_ROW };
for (int i=0; i<optionsCount; i++) {
mstate_tab[2+i] = getZoneOptionColumns(&options[i]);
}
mstate_tab[2+optionsCount] = 0; // The button for the Topbar setup
CUSTOM_MENU_WITH_OPTIONS(STR_USER_INTERFACE, THEME_ICONS, menuTabScreensSetup, menuPageCount, 0, linesCount);
for (int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_CONTENT_TOP + i * FH;
int k = i + menuVerticalOffset;
LcdFlags blink = ((s_editMode > 0) ? BLINK | INVERS : INVERS);
LcdFlags attr = (menuVerticalPosition == k ? blink : 0);
switch (k) {
case ITEM_SCREEN_SETUP_THEME: {
lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, STR_THEME);
Theme * new_theme = editThemeChoice<Theme>(SCREENS_SETUP_2ND_COLUMN, y, getRegisteredThemes(), theme, needsOffsetCheck, attr, event);
if (new_theme) {
new_theme->init();
loadTheme(new_theme);
strncpy(g_eeGeneral.themeName, new_theme->getName(), sizeof(g_eeGeneral.themeName));
killEvents(KEY_ENTER);
storageDirty(EE_GENERAL);
}
break;
}
case ITEM_SCREEN_SETUP_THEME+1:
break;
default:
{
uint8_t index = k - ITEM_SCREEN_SETUP_THEME_OPTION1;
if (index < optionsCount) {
const ZoneOption * option = &options[index];
ZoneOptionValue * value = theme->getOptionValue(index);
bool ret = editZoneOption(y, option, value, attr, EE_GENERAL, event);
if (option->type == ZoneOption::Color) {
if (attr && event == EVT_KEY_FIRST(KEY_EXIT)) {
theme->update();
}
}
else if (ret) {
theme->update();
}
}
else if (index == optionsCount) {
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TOP_BAR);
drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_SETUP, attr);
if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
currentScreen = customScreens[0];
currentContainer = topbar;
pushMenu(menuWidgetsSetup);
}
}
break;
}
}
}
return true;
}
enum MenuScreenSetupItems {
ITEM_SCREEN_SETUP_LAYOUT,
ITEM_SCREEN_SETUP_WIDGETS_SETUP = ITEM_SCREEN_SETUP_LAYOUT+2,
ITEM_SCREEN_SETUP_LAYOUT_OPTION1,
};
bool menuScreenSetup(int index, event_t event)
{
if (customScreens[index] == nullptr) {
return menuScreenAdd(event);
}
currentScreen = customScreens[index];
currentContainer = currentScreen;
bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0);
char title[] = TR_MAIN_VIEW_X;
title[sizeof(title)-2] = '1' + index;
menuPageCount = updateMainviewsMenu();
const ZoneOption * options = currentScreen->getFactory()->getOptions();
int optionsCount = getOptionsCount(options);
linesCount = ITEM_SCREEN_SETUP_LAYOUT_OPTION1 + optionsCount;
if (menuPageCount > 3)
++linesCount;
uint8_t mstate_tab[2 + MAX_LAYOUT_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE | uint8_t(getRegisteredLayouts().size()-1)), ORPHAN_ROW };
for (int i=0; i<optionsCount; i++) {
mstate_tab[3+i] = getZoneOptionColumns(&options[i]);
}
mstate_tab[3+optionsCount] = 0; // The remove button
CUSTOM_MENU_WITH_OPTIONS(title, THEME_ICONS, menuTabScreensSetup, menuPageCount, index+1, linesCount);
for (int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_CONTENT_TOP + i * FH;
int k = i + menuVerticalOffset;
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
LcdFlags attr = (menuVerticalPosition == k ? blink : 0);
switch(k) {
case ITEM_SCREEN_SETUP_LAYOUT:
{
lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, STR_LAYOUT);
const LayoutFactory * factory = editThemeChoice<const LayoutFactory>(SCREENS_SETUP_2ND_COLUMN, y, getRegisteredLayouts(), currentScreen->getFactory(), needsOffsetCheck, attr, event);
if (factory) {
delete customScreens[index];
currentScreen = customScreens[index] = factory->create(&g_model.screenData[index].layoutData);
strncpy(g_model.screenData[index].layoutName, factory->getName(), sizeof(g_model.screenData[index].layoutName));
killEvents(KEY_ENTER);
storageDirty(EE_MODEL);
}
break;
}
case ITEM_SCREEN_SETUP_LAYOUT+1:
break;
case ITEM_SCREEN_SETUP_WIDGETS_SETUP:
drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_SETUP_WIDGETS, attr);
if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
pushMenu(menuWidgetsSetup);
}
break;
default:
{
uint8_t o = k - ITEM_SCREEN_SETUP_LAYOUT_OPTION1;
if (o < optionsCount) {
const ZoneOption * option = &options[o];
ZoneOptionValue * value = currentScreen->getOptionValue(o);
if (editZoneOption(y, option, value, attr, EE_MODEL, event)) {
currentScreen->update();
loadCustomScreens();
}
}
else if (menuPageCount > 3 && o == optionsCount) {
drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_REMOVE_SCREEN, attr);
if (attr && event == EVT_KEY_LONG(KEY_ENTER)) {
delete currentScreen;
if (index != MAX_CUSTOM_SCREENS-1) {
memmove(&g_model.screenData[index], &g_model.screenData[index + 1], sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - index - 1));
memmove(&customScreens[index], &customScreens[index + 1], sizeof(Layout *) * (MAX_CUSTOM_SCREENS - index - 1));
}
memset(&g_model.screenData[MAX_CUSTOM_SCREENS-1], 0, sizeof(CustomScreenData));
customScreens[MAX_CUSTOM_SCREENS-1] = nullptr;
loadCustomScreens();
killEvents(KEY_ENTER);
chainMenu(menuTabScreensSetup[index > 0 ? index : 1]);
return false;
}
}
break;
}
}
}
return true;
}
template<int N>
bool menuCustomScreenSetup(event_t event)
{
return menuScreenSetup(N, event);
}
const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] = {
menuScreensTheme,
menuCustomScreenSetup<0>,
menuCustomScreenSetup<1>,
menuCustomScreenSetup<2>,
menuCustomScreenSetup<3>,
menuCustomScreenSetup<4>
};
int updateMainviewsMenu()
{
for (int index=1; index<MAX_CUSTOM_SCREENS; index++) {
if (customScreens[index]) {
THEME_ICONS[2+index] = ICON_THEME_VIEW1+index;
}
else {
THEME_ICONS[2+index] = ICON_THEME_ADD_VIEW;
return 2+index;
}
}
return 1+MAX_CUSTOM_SCREENS;
}
bool menuScreenAdd(event_t event)
{
menuPageCount = updateMainviewsMenu();
if (event == EVT_KEY_FIRST(KEY_ENTER) && getRegisteredLayouts().size()) {
const LayoutFactory * lf = getRegisteredLayouts().front();
customScreens[menuPageCount-2] = lf->create(&g_model.screenData[menuPageCount-2].layoutData);
strncpy(g_model.screenData[menuPageCount-2].layoutName, lf->getName(), sizeof(g_model.screenData[menuPageCount-2].layoutName));
s_editMode = 0;
menuHorizontalPosition = -1;
killEvents(KEY_ENTER);
storageDirty(EE_MODEL);
return false;
}
SIMPLE_MENU_WITH_OPTIONS(STR_ADD_MAIN_VIEW, THEME_ICONS, menuTabScreensSetup, menuPageCount, menuPageCount-1, 0);
return true;
}
@@ -20,6 +20,7 @@
#include "special_functions.h"
#include "opentx.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(functions == g_model.customFn ? EE_MODEL : EE_GENERAL)
+21
View File
@@ -27,6 +27,8 @@
#include "opentx.h" // TODO for constants...
#include <algorithm>
TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon):
FormGroup(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE),
#if defined(HARDWARE_TOUCH)
@@ -94,6 +96,19 @@ void TabsGroup::addTab(PageTab * page)
setCurrentTab(0);
}
header.carousel.updateInnerWidth();
invalidate();
}
int TabsGroup::removeTab(PageTab * page)
{
auto tabIter = std::find(tabs.begin(), tabs.end(), page);
if (tabIter != tabs.end()) {
auto idx = tabIter - tabs.begin();
removeTab(idx);
return idx;
}
return -1;
}
void TabsGroup::removeTab(unsigned index)
@@ -103,6 +118,7 @@ void TabsGroup::removeTab(unsigned index)
}
tabs.erase(tabs.begin() + index);
header.carousel.updateInnerWidth();
invalidate();
}
void TabsGroup::removeAllTabs()
@@ -132,6 +148,11 @@ void TabsGroup::setVisibleTab(PageTab * tab)
}
}
// TODO: add a mechanism to trigger a rebuild of a PageTab
// -> rebuild():
// - as in setVisibleTab() (clear & build)
// - but without changing tab
void TabsGroup::checkEvents()
{
Window::checkEvents();
+10 -1
View File
@@ -193,8 +193,17 @@ class TabsGroup: public Window
}
#endif
unsigned getTabs() const
{
return tabs.size();
}
void addTab(PageTab * page);
// Return the index of the found tab
// or -1 if the tab could not be found
int removeTab(PageTab * page);
void removeTab(unsigned index);
void removeAllTabs();
@@ -206,7 +215,7 @@ class TabsGroup: public Window
setVisibleTab(tabs[index]);
}
}
void checkEvents() override;
#if defined(HARDWARE_KEYS)
+49
View File
@@ -0,0 +1,49 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "libopenui.h"
#include "textedit.h"
#include "storage/storage.h"
class ModelTextEdit: public TextEdit
{
public:
ModelTextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0):
TextEdit(parent, rect, value, length, windowFlags)
{
setChangeHandler([](){
storageDirty(EE_MODEL);
});
}
};
class RadioTextEdit: public TextEdit
{
public:
RadioTextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0):
TextEdit(parent, rect, value, length, windowFlags)
{
setChangeHandler([](){
storageDirty(EE_GENERAL);
});
}
};
+2
View File
@@ -19,6 +19,8 @@
*/
#include "opentx.h"
#include "libopenui.h"
//#include "checkbox.h"
//#include "modal_window.h"
@@ -316,7 +316,7 @@ class Theme480: public OpenTxTheme
void drawBackground(BitmapBuffer * dc) const override
{
if (backgroundBitmap) {
dc->drawBitmap(0, 0, backgroundBitmap);
dc->drawBitmap(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), backgroundBitmap);
}
else {
lcdSetColor(g_eeGeneral.themeData.options[0].value.unsignedValue);
+50 -122
View File
@@ -19,139 +19,67 @@
*/
#include "opentx.h"
#include "layer.h"
#include "view_main.h"
#include "screen_setup.h"
#include "storage/storage.h"
#include "topbar.h"
#include "topbar_impl.h"
TopBar::TopBar(Window * parent) :
WidgetsContainer<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS>({0, 0, LCD_W, MENU_HEADER_HEIGHT}, &g_model.topbarData)
Topbar * TopbarFactory::create(Window * parent)
{
attach(parent);
return new TopbarImpl(parent);
}
unsigned int TopBar::getZonesCount() const
SetupTopBarWidgetsPage::SetupTopBarWidgetsPage(ScreenMenu* menu):
FormWindow(ViewMain::instance(), {0, 0, 0, 0}, FORM_FORWARD_FOCUS),
menu(menu)
{
return MAX_TOPBAR_ZONES;
// remember focus
Layer::push(this);
auto viewMain = ViewMain::instance();
// save current view & switch to 1st one
savedView = viewMain->getCurrentMainView();
viewMain->setCurrentMainView(0);
viewMain->bringToTop();
//TODO: force the topbar to be visible?
// adopt the dimensions of the main view
setRect(viewMain->getRect());
auto topbar = dynamic_cast<TopbarImpl*>(viewMain->getTopbar());
for (unsigned i = 0; i < topbar->getZonesCount(); i++) {
auto rect = topbar->getZone(i);
auto widget = new SetupWidgetsPageSlot(this, rect, topbar, i);
if (i == 0) widget->setFocus();
}
}
rect_t TopBar::getZone(unsigned int index) const
void SetupTopBarWidgetsPage::deleteLater(bool detach, bool trash)
{
return {
coord_t(49 + (TOPBAR_ZONE_WIDTH + 2 * TOPBAR_ZONE_MARGIN) * index),
TOPBAR_ZONE_MARGIN,
TOPBAR_ZONE_WIDTH,
TOPBAR_HEIGHT
};
// restore screen setting tab on top
menu->bringToTop();
Layer::pop(this);
// and continue async deletion...
FormWindow::deleteLater(detach, trash);
storageDirty(EE_MODEL);
}
const char * const STR_MONTHS[] = TR_MONTHS;
void TopBar::paint(BitmapBuffer * dc)
#if defined(HARDWARE_KEYS)
void SetupTopBarWidgetsPage::onEvent(event_t event)
{
dc->drawSolidFilledRect(0, 0, width(), height(), MENU_BGCOLOR);
OpenTxTheme::instance()->drawTopLeftBitmap(dc);
struct gtm t;
gettime(&t);
char str[10];
sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]);
dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, MENU_COLOR);
dc->drawText(DATETIME_MIDDLE, DATETIME_LINE1, str, FONT(XS) | CENTERED| MENU_COLOR);
getTimerString(str, getValue(MIXSRC_TX_TIME));
dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | MENU_COLOR);
// USB icon
if (usbPlugged()) {
dc->drawBitmapPattern(LCD_W - 98, 8, LBM_TOPMENU_USB, MENU_COLOR);
}
// Logs
if (isFunctionActive(FUNCTION_LOGS) && !usbPlugged() && BLINK_ON_PHASE) {
dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, MENU_COLOR);
}
// RSSI
const uint8_t rssiBarsValue[] = {30, 40, 50, 60, 80};
const uint8_t rssiBarsHeight[] = {5, 10, 15, 21, 31};
for (unsigned int i = 0; i < DIM(rssiBarsHeight); i++) {
uint8_t height = rssiBarsHeight[i];
dc->drawSolidFilledRect(LCD_W - 90 + i * 6, 38 - height, 4, height, TELEMETRY_RSSI() >= rssiBarsValue[i] ? MENU_COLOR : MENU_TITLE_DISABLE_COLOR);
}
#if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) {
dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, MENU_COLOR);
switch (event) {
case EVT_KEY_BREAK(KEY_EXIT):
killEvents(event);
deleteLater();
break;
}
}
#endif
/* Audio volume */
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_SCALE, MENU_TITLE_DISABLE_COLOR);
if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_0, MENU_COLOR);
else if (requiredSpeakerVolume < 7)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, MENU_COLOR);
else if (requiredSpeakerVolume < 13)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, MENU_COLOR);
else if (requiredSpeakerVolume < 19)
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, MENU_COLOR);
else
dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, MENU_COLOR);
/* Tx battery */
uint8_t bars = GET_TXBATT_BARS(5);
#if defined(USB_CHARGER)
if (usbChargerLed()) {
dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT_CHARGING, MENU_COLOR);
}
else {
dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR);
}
#else
dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR);
#endif
for (unsigned int i = 0; i < 5; i++) {
dc->drawSolidFilledRect(LCD_W - 128 + 4 * i, 30, 2, 8, i >= bars ? MENU_TITLE_DISABLE_COLOR : MENU_COLOR);
}
#if 0
// Radio battery - TODO
// drawValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|FONT(XS)|MENU_COLOR);
// lcdDrawSolidRect(300, 3, 20, 50, MENU_COLOR);
// lcdDrawRect(batt_icon_x+FW, BAR_Y+1, 13, 7);
// lcdDrawSolidVerticalLine(batt_icon_x+FW+13, BAR_Y+2, 5);
// Rx battery
if (g_model.voltsSource) {
TelemetryItem & item = telemetryItems[g_model.voltsSource-1];
if (item.isAvailable()) {
int32_t value = item.value;
TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1];
LcdFlags att = 0;
if (sensor.prec == 2) {
att |= PREC1;
value /= 10;
}
else if (sensor.prec == 1) {
att |= PREC1;
}
att |= (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR);
lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR);
lcdDrawText(ALTITUDE_X+PADDING, VOLTS_Y+2, "Voltage", att);
drawValueWithUnit(ALTITUDE_X+PADDING, VOLTS_Y+12, value, UNIT_VOLTS, FONT(XL)|LEFT|att);
}
}
// Model altitude
if (g_model.frsky.altitudeSource) {
TelemetryItem & item = telemetryItems[g_model.frsky.altitudeSource-1];
if (item.isAvailable()) {
int32_t value = item.value;
TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1];
if (sensor.prec) value /= sensor.prec == 2 ? 100 : 10;
LcdFlags att = (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR);
lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR);
lcdDrawText(ALTITUDE_X+PADDING, ALTITUDE_Y+2, "Alt", att);
drawValueWithUnit(ALTITUDE_X+PADDING, ALTITUDE_Y+12, value, UNIT_METERS, FONT(XL)|LEFT|att);
}
}
#endif
}
+36 -20
View File
@@ -18,32 +18,48 @@
* GNU General Public License for more details.
*/
#ifndef _TOPBAR_H_
#define _TOPBAR_H_
#pragma once
#include "widgets_container.h"
#include "layout.h"
#define MAX_TOPBAR_ZONES 4
#define MAX_TOPBAR_OPTIONS 1 // just because of VC++ which doesn't like 0-size arrays :(
#define TOPBAR_ZONE_WIDTH 70
constexpr coord_t TOPBAR_ZONE_WIDTH = 70;
constexpr coord_t TOPBAR_ZONE_MARGIN = 3;
constexpr coord_t TOPBAR_HEIGHT = MENU_HEADER_HEIGHT - 2 * TOPBAR_ZONE_MARGIN;
class TopBar: public WidgetsContainer<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS>
class ScreenMenu;
class TopbarImpl;
class Topbar
{
public:
explicit TopBar(Window * parent);
unsigned int getZonesCount() const override;
rect_t getZone(unsigned int index) const override;
void paint(BitmapBuffer * dc) override;
void checkEvents() override
{
invalidate();
}
virtual void load() = 0;
};
#endif // _TOPBAR_H_
class TopbarFactory
{
public:
static Topbar * create(Window * parent);
};
class SetupTopBarWidgetsPage: public FormWindow
{
public:
explicit SetupTopBarWidgetsPage(ScreenMenu* menu);
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "SetupTopBarWidgetsPage";
}
#endif
void deleteLater(bool detach = true, bool trash = true) override;
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override;
#endif
protected:
ScreenMenu* menu;
unsigned savedView = 0;
};
@@ -20,6 +20,7 @@
#include "opentx.h"
#include "view_logical_switches.h"
#include "gridlayout.h"
#include <utility>
@@ -21,6 +21,7 @@
#pragma once
#include "opentx.h"
#include "tabsgroup.h"
constexpr coord_t CSW_1ST_COLUMN = 50;
constexpr coord_t CSW_2ND_COLUMN = 120;
+215 -54
View File
@@ -22,46 +22,20 @@
#include "menu_model.h"
#include "menu_radio.h"
#include "menu_screen.h"
// #include "menu_screens.h"
#include "model_select.h"
#include "view_channels.h"
#include "view_statistics.h"
#include "topbar_impl.h"
#include "menu.h"
#include "opentx.h"
Layout * customScreens[MAX_CUSTOM_SCREENS] = {};
void onMainViewMenu(const char *result)
{
/*if (result == STR_VIEW_NOTES) {
pushModelNotes();
}
else if (result == STR_STATISTICS) {
pushMenu(menuTabStats[0]);
}
else if (result == STR_ABOUT_US) {
chainMenu(menuAboutView);
}
else if (result == STR_MONITOR_SCREENS) {
pushMenu(menuTabMonitors[lastMonitorPage]);
}*/
}
int getMainViewsCount()
{
for (int index=1; index<MAX_CUSTOM_SCREENS; index++) {
if (!customScreens[index]) {
return index;
}
}
return MAX_CUSTOM_SCREENS;
}
ViewMain * ViewMain::_instance = nullptr;
ViewMain::ViewMain():
FormWindow(MainWindow::instance(), { 0, 0, LCD_W, LCD_H })
Window(MainWindow::instance(), MainWindow::instance()->getRect()),
topbar(dynamic_cast<TopbarImpl*>(TopbarFactory::create(this)))
{
#if defined(HARDWARE_TOUCH) && !defined(HARDWARE_KEYS)
new FabButton(this, 50, 100, ICON_MODEL,
[=]() -> uint8_t {
@@ -88,6 +62,7 @@ ViewMain::ViewMain():
}, NO_FOCUS);
#endif
setPageWidth(getParent()->width());
focusWindow = this;
}
@@ -95,6 +70,196 @@ ViewMain::~ViewMain()
{
}
void ViewMain::setTopbarVisible(float visible)
{
topbar->setVisible(visible);
}
unsigned ViewMain::getMainViewsCount() const
{
return views;
}
void ViewMain::setMainViewsCount(unsigned views)
{
if (views > MAX_CUSTOM_SCREENS)
views = MAX_CUSTOM_SCREENS;
// update number of views
this->views = views;
// adjust current screen if needed
if (g_model.view >= views) {
setCurrentMainView(views - 1);
}
setInnerWidth(getParent()->width() * views);
}
coord_t ViewMain::getMainViewLeftPos(unsigned view) const
{
return getParent()->width() * view;
}
rect_t ViewMain::getMainZone(rect_t zone, bool hasTopbar) const
{
auto visibleHeight = topbar->getVisibleHeight(hasTopbar ? 1.0 : 0.0);
zone.y += visibleHeight;
zone.h -= visibleHeight;
return zone;
}
unsigned ViewMain::getCurrentMainView() const
{
return g_model.view;
}
void ViewMain::setCurrentMainView(unsigned view)
{
if (view < getMainViewsCount()) {
setScrollPositionX(view * pageWidth);
TRACE("### switched to view #%i", g_model.view);
}
}
void ViewMain::nextMainView()
{
auto view = getCurrentMainView();
if (++view >= getMainViewsCount())
view = 0;
setCurrentMainView(view);
}
void ViewMain::previousMainView()
{
auto view = getCurrentMainView();
if (view > 0)
view--;
else
view = getMainViewsCount() - 1;
setCurrentMainView(view);
}
Topbar* ViewMain::getTopbar()
{
return topbar;
}
static bool hasTopbar(unsigned view)
{
if (view < sizeof(g_model.screenData)) {
const auto& layoutData = g_model.screenData[view].layoutData;
return layoutData.options[LAYOUT_OPTION_TOPBAR].value.boolValue;
}
return false;
}
void ViewMain::updateTopbarVisibility()
{
// relative to left visible page
int leftScroll = getScrollPositionX() % pageWidth;
if (leftScroll == 0) {
setTopbarVisible(hasTopbar(g_model.view));
customScreens[g_model.view]->adjustLayout();
}
else {
int leftIdx = getScrollPositionX() / pageWidth;
bool leftTopbar = hasTopbar(leftIdx);
bool rightTopbar = hasTopbar(leftIdx+1);
if (leftTopbar != rightTopbar) {
float ratio = (float)leftScroll / (float)pageWidth;
if (leftTopbar) {
// scrolling from a screen with Topbar
ratio = 1.0 - ratio;
}
else {
// scrolling to a screen with Topbar
// -> ratio is ok
}
setTopbarVisible(ratio);
customScreens[leftIdx]->adjustLayout();
customScreens[leftIdx+1]->adjustLayout();
}
}
}
void ViewMain::setScrollPositionX(coord_t value)
{
Window::setScrollPositionX(value);
topbar->setLeft(getScrollPositionX());
// update page index
g_model.view = getPageIndex();
// update Topbar
updateTopbarVisibility();
}
void ViewMain::setScrollPositionY(coord_t value)
{
// this one is not used yet, but could
// usefull to place the screens upwards
Window::setScrollPositionY(value);
topbar->setTop(getScrollPositionY());
}
#if defined(HARDWARE_TOUCH)
//#define DEBUG_SLIDE
bool ViewMain::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY)
{
// prevent screen sliding over the next one (one screen at a time)
if (slidingWindow == this) {
if (prevSlideState != touchState.event) {
if (touchState.event == TE_SLIDE_END) {
startSlidePage = getCurrentMainView();
#if defined(DEBUG_SLIDE)
TRACE("### startSlidePage = %d ###", startSlidePage);
#endif
}
prevSlideState = touchState.event;
}
else if (prevSlideState == TE_SLIDE_END){
// let's check what would be the next position
auto nextPos = (getScrollPositionX() - slideX);
// if we would get over more than one page counting from slide-start
if (abs(nextPos - getMainViewLeftPos(startSlidePage)) > pageWidth) {
#if defined(DEBUG_SLIDE)
TRACE("### kill the move ###");
#endif
// kill the movement and let "snap-to-view" finish the job
prevSlideState = TE_NONE;
touchState.event = TE_NONE;
touchState.lastDeltaX = 0;
touchState.lastDeltaY = 0;
return true;
}
}
}
#if defined(DEBUG_SLIDE)
TRACE("%sWindow[scrollX=%d, scrollY=%d]->onTouchSlide[x=%d, y=%d, startX=%d, startY=%d, slideX=%d, slideY=%d]",
touchState.event == TE_SLIDE_END && slidingWindow ? "###" : "",
getScrollPositionX(), getScrollPositionY(),
x, y, startX, startY, slideX, slideY);
#endif
return Window::onTouchSlide(x, y, startX, startY, slideX, slideY);
}
#endif
#if defined(HARDWARE_KEYS)
void ViewMain::onEvent(event_t event)
{
@@ -118,6 +283,22 @@ void ViewMain::onEvent(event_t event)
killEvents(event);
openMenu();
break;
case EVT_KEY_BREAK(KEY_PGDN):
killEvents(event);
nextMainView();
break;
//TODO: these need to go away!
// -> board code should map the keys as required
#if defined(KEYS_GPIO_REG_UP)
case EVT_KEY_BREAK(KEY_PGUP):
#else
case EVT_KEY_LONG(KEY_PGDN):
#endif
killEvents(event);
previousMainView();
break;
}
}
#endif
@@ -164,32 +345,12 @@ void ViewMain::openMenu()
void ViewMain::paint(BitmapBuffer * dc)
{
TRACE_WINDOWS("### ViewMain::paint(offset_x=%d;offset_y=%d) ###",
dc->getOffsetX(), dc->getOffsetY());
OpenTxTheme::instance()->drawBackground(dc);
if (g_model.view >= getMainViewsCount()) {
g_model.view = 0;
}
//
// for (uint8_t i=0; i<MAX_CUSTOM_SCREENS; i++) {
// if (customScreens[i]) {
// if (i == g_model.view) {
// customScreens[i]->refresh(); }
// else
// customScreens[i]->background();
// }
// }
}
#if 0
bool menuMainViewChannelsMonitor(event_t event)
{
switch (event) {
case EVT_KEY_BREAK(KEY_EXIT):
chainMenu(menuMainView);
event = 0;
return false;
}
return menuChannelsView(event);
}
#endif
+47 -1
View File
@@ -21,9 +21,15 @@
#ifndef _VIEW_MAIN_H_
#define _VIEW_MAIN_H_
#include <memory>
#include "form.h"
#include "topbar.h"
class ViewMain: public FormWindow
class SetupWidgetsPage;
class SetupTopBarWidgetsPage;
class ViewMain: public Window
{
// singleton
explicit ViewMain();
@@ -46,13 +52,53 @@ class ViewMain: public FormWindow
}
#endif
void updateTopbarVisibility();
// Get the available space in the middle of the screen
// (without topbar)
rect_t getMainZone(rect_t zone, bool hasTopbar) const;
unsigned getMainViewsCount() const;
void setMainViewsCount(unsigned views);
coord_t getMainViewLeftPos(unsigned view) const;
unsigned getCurrentMainView() const;
void setCurrentMainView(unsigned view);
void nextMainView();
void previousMainView();
Topbar* getTopbar();
protected:
static ViewMain * _instance;
unsigned views = 0;
TopbarImpl* topbar = nullptr;
// Widget setup requires special permissions ;-)
friend class SetupWidgetsPage;
friend class SetupTopBarWidgetsPage;
// Set topbar visibility [0.0 -> 1.0]
void setTopbarVisible(float visible);
void setScrollPositionX(coord_t value) override;
void setScrollPositionY(coord_t value) override;
#if defined(HARDWARE_TOUCH)
unsigned char prevSlideState = 0;
unsigned int startSlidePage = 0;
bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override;
#endif
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override;
#endif
void paint(BitmapBuffer * dc) override;
void openMenu();
};
@@ -0,0 +1,298 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "view_main_decoration.h"
#include "layout.h"
// because of IS_POT_MULTIPOS() and pal
#include "opentx.h"
// these 2 need things from opentx.h
#include "layouts/sliders.h"
#include "layouts/trims.h"
#include "board.h"
void ViewMainDecoration::setSlidersVisible(bool visible)
{
//
// Horizontal Sliders
//
sliders[SLIDERS_POT1]->setHeight(visible ? TRIM_SQUARE_SIZE : 0);
if (IS_POT_MULTIPOS(POT2)) {
sliders[SLIDERS_POT2]->setHeight(visible ? MULTIPOS_H : 0);
}
else if (IS_POT(POT2)) {
sliders[SLIDERS_POT2]->setHeight(visible ? TRIM_SQUARE_SIZE : 0);
}
#if defined(HARDWARE_POT3)
sliders[SLIDERS_POT3]->setHeight(visible ? TRIM_SQUARE_SIZE : 0);
#endif
//
// Vertical sliders
//
sliders[SLIDERS_REAR_LEFT]->setWidth(visible ? TRIM_SQUARE_SIZE : 0);
#if defined(HARDWARE_EXT1)
if (IS_POT_SLIDER_AVAILABLE(EXT1)) {
sliders[SLIDERS_EXT1]->setWidth(visible ? TRIM_SQUARE_SIZE : 0);
}
#endif
sliders[SLIDERS_REAR_RIGHT]->setWidth(visible ? TRIM_SQUARE_SIZE : 0);
#if defined(HARDWARE_EXT2)
if (IS_POT_SLIDER_AVAILABLE(EXT2)) {
sliders[SLIDERS_EXT2]->setWidth(visible ? TRIM_SQUARE_SIZE : 0);
}
#endif
}
void ViewMainDecoration::setTrimsVisible(bool visible)
{
trims[TRIMS_LH]->setHeight(visible ? TRIM_SQUARE_SIZE : 0);
trims[TRIMS_RH]->setHeight(visible ? TRIM_SQUARE_SIZE : 0);
trims[TRIMS_LV]->setWidth(visible ? TRIM_SQUARE_SIZE : 0);
trims[TRIMS_RV]->setWidth(visible ? TRIM_SQUARE_SIZE : 0);
}
void ViewMainDecoration::setFlightModeVisible(bool visible)
{
flightMode->setHeight(visible ? FM_LABEL_HEIGHT : 0);
}
void ViewMainDecoration::adjustDecoration()
{
// Sliders are closer to the edge and must be computed first
// These are located on the bottom
auto pos = bottom() - sliders[SLIDERS_POT1]->height();
sliders[SLIDERS_POT1]->setTop(pos);
if (sliders[SLIDERS_POT2]) {
auto sl = sliders[SLIDERS_POT2];
sl->setTop(pos);
if (IS_POT_MULTIPOS(POT2)) {
sl->setWidth(MULTIPOS_W);
}
else { // if !IS_POT(POT2) -> sliders[SLIDERS_POT2] == nullptr
sl->setWidth(HORIZONTAL_SLIDERS_WIDTH);
}
sl->setLeft((width() - sl->width()) / 2);
}
#if defined(HARDWARE_POT3)
sliders[SLIDERS_POT3]->setTop(pos);
#endif
// Horizontal trims are on top of horizontal sliders
pos -= trims[TRIMS_LH]->height();
trims[TRIMS_LH]->setTop(pos);
trims[TRIMS_RH]->setTop(pos);
// Vertical trims/slider are on top of horizontal sliders with a small margin
auto vertTop = pos - HMARGIN - VERTICAL_SLIDERS_HEIGHT;
// Left side (vertical)
pos = left();
sliders[SLIDERS_REAR_LEFT]->setLeft(pos);
sliders[SLIDERS_REAR_LEFT]->setTop(vertTop);
#if defined(HARDWARE_EXT1)
sliders[SLIDERS_EXT1]->setLeft(pos);
if (IS_POT_SLIDER_AVAILABLE(EXT1)) {
auto rl = sliders[SLIDERS_REAR_LEFT];
auto e1 = sliders[SLIDERS_EXT1];
// If EXT1 is configured as a slider,
// place it bellow rear-left slider (and make them smaller)
rl->setHeight(VERTICAL_SLIDERS_HEIGHT / 2);
e1->setTop(vertTop + rl->height() + HMARGIN/2);
e1->setHeight(rl->height());
}
else {
auto rl = sliders[SLIDERS_REAR_LEFT];
auto e1 = sliders[SLIDERS_EXT1];
// Otherwise hide the extra slider and make rear-left fullsize
rl->setHeight(VERTICAL_SLIDERS_HEIGHT);
e1->setHeight(0);
}
#endif
// Right side (vertical)
pos = right() - sliders[SLIDERS_REAR_RIGHT]->width();
sliders[SLIDERS_REAR_RIGHT]->setLeft(pos);
sliders[SLIDERS_REAR_RIGHT]->setTop(vertTop);
#if defined(HARDWARE_EXT2)
sliders[SLIDERS_EXT2]->setLeft(pos);
if (IS_POT_SLIDER_AVAILABLE(EXT2)) {
auto rr = sliders[SLIDERS_REAR_RIGHT];
auto e2 = sliders[SLIDERS_EXT2];
// If EXT2 is configured as a slider,
// place it bellow rear-left slider (and make them smaller)
rr->setHeight(VERTICAL_SLIDERS_HEIGHT / 2);
e2->setTop(vertTop + rr->height() + HMARGIN/2);
e2->setHeight(rr->height());
}
else {
auto rr = sliders[SLIDERS_REAR_RIGHT];
auto e2 = sliders[SLIDERS_EXT2];
// Otherwise hide the extra slider and make rear-left fullsize
rr->setHeight(VERTICAL_SLIDERS_HEIGHT);
e2->setHeight(0);
}
#endif
// Finally place the vertical trims further from the edge
trims[TRIMS_LV]->setLeft(sliders[SLIDERS_REAR_LEFT]->right());
trims[TRIMS_LV]->setTop(vertTop);
trims[TRIMS_RV]->setLeft(sliders[SLIDERS_REAR_RIGHT]->left() - trims[TRIMS_RV]->width());
trims[TRIMS_RV]->setTop(vertTop);
// Place the flight-mode text box
// -> between horiz trims (if existing)
// else on top of horiz sliders
//
pos = trims[TRIMS_LH]->right();
flightMode->setLeft(pos);
pos = trims[TRIMS_RH]->left() - pos;
flightMode->setWidth(pos);
pos = sliders[SLIDERS_POT1]->top();
pos -= flightMode->height();
flightMode->setTop(pos);
}
rect_t ViewMainDecoration::getMainZone() const
{
rect_t zone = {
trims[TRIMS_LV]->right() + MAIN_ZONE_BORDER,
/*topbar->bottom() +*/ MAIN_ZONE_BORDER,
0, 0 // let's compute them!
};
zone.w = trims[TRIMS_RV]->left() - MAIN_ZONE_BORDER - zone.x;
// min(trims[TRIMS_LH]->top(), flightMode->top())
if (trims[TRIMS_LH]->top() < flightMode->top())
zone.h = trims[TRIMS_LH]->top();
else
zone.h = flightMode->top();
zone.h -= MAIN_ZONE_BORDER + zone.y;
return zone;
}
void ViewMainDecoration::createSliders()
{
// fixed size array, so that works
memset(sliders, 0, sizeof(sliders));
rect_t r = {
// left
HMARGIN, 0,
HORIZONTAL_SLIDERS_WIDTH, 0
};
sliders[SLIDERS_POT1] = new MainViewHorizontalSlider(this, r, CALIBRATED_POT1);
r = rect_t { 0, 0, 0, 0 };
if (IS_POT_MULTIPOS(POT2)) {
sliders[SLIDERS_POT2] = new MainView6POS(this, r, 1);
}
else if (IS_POT(POT2)) {
sliders[SLIDERS_POT2] = new MainViewHorizontalSlider(this, r, CALIBRATED_POT2);
}
#if defined(HARDWARE_POT3)
r = rect_t {
// right
width() - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, 0,
HORIZONTAL_SLIDERS_WIDTH, 0
};
sliders[SLIDERS_POT3] = new MainViewHorizontalSlider(this, r, CALIBRATED_POT3);
#endif
r = rect_t { 0, 0, 0, 0 };
sliders[SLIDERS_REAR_LEFT] = new MainViewVerticalSlider(this, r, CALIBRATED_SLIDER_REAR_LEFT);
sliders[SLIDERS_REAR_RIGHT] = new MainViewVerticalSlider(this, r, CALIBRATED_SLIDER_REAR_RIGHT);
#if defined(HARDWARE_EXT1)
sliders[SLIDERS_EXT1] = new MainViewVerticalSlider(this, r, CALIBRATED_POT_EXT1);
#endif
#if defined(HARDWARE_EXT2)
sliders[SLIDERS_EXT2] = new MainViewVerticalSlider(this, r, CALIBRATED_POT_EXT2);
#endif
}
void ViewMainDecoration::createTrims()
{
// Trim order TRIM_LH, TRIM_LV, TRIM_RV, TRIM_RH
rect_t r = {
left() + HMARGIN, 0,
HORIZONTAL_SLIDERS_WIDTH, 0
};
trims[TRIMS_LH] = new MainViewHorizontalTrim(this, r, 0);
r = rect_t {
right() - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, 0,
HORIZONTAL_SLIDERS_WIDTH, 0
};
trims[TRIMS_RH] = new MainViewHorizontalTrim(this, r, 3);
r = rect_t {
0, 0, 0,
VERTICAL_SLIDERS_HEIGHT
};
trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 2);
trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 1);
}
void ViewMainDecoration::createFlightMode()
{
rect_t r = {
// centered text box (50 pixels from either edge)
// -> re-size once the other components are set
50, 0, width() - 100, 0
};
std::function<std::string()> getFM = []() -> std::string {
return g_model.flightModeData[mixerCurrentFlightMode].name;
};
flightMode = new DynamicText(this, r, getFM, CENTERED);
}
@@ -0,0 +1,85 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "window.h"
constexpr coord_t FM_LABEL_HEIGHT = 20;
class ViewMainDecoration: public Window
{
public:
ViewMainDecoration(Window * parent, const rect_t & rect):
Window(parent, rect)
{
createSliders();
createTrims();
createFlightMode();
}
// Set decoration visibility
void setTrimsVisible(bool visible);
void setSlidersVisible(bool visible);
void setFlightModeVisible(bool visible);
// Re-calculate positions
void adjustDecoration();
// Get the available space in the middle of the screen
// (without decoration)
rect_t getMainZone() const;
protected:
#if defined(DEBUG_WINDOWS)
virtual std::string getName() const
{
return "ViewMainDecoration";
}
#endif
enum {
SLIDERS_POT1 = 0,
SLIDERS_POT2,
SLIDERS_POT3,
SLIDERS_REAR_LEFT,
SLIDERS_EXT1,
SLIDERS_REAR_RIGHT,
SLIDERS_EXT2,
SLIDERS_MAX
};
enum {
TRIMS_LH = 0,
TRIMS_LV,
TRIMS_RV,
TRIMS_RH,
TRIMS_MAX
};
Window* sliders[SLIDERS_MAX];
Window* trims[TRIMS_MAX];
Window* flightMode;
void createSliders();
void createTrims();
void createFlightMode();
};
+3 -1
View File
@@ -23,6 +23,8 @@
#include "opentx.h"
#include "tabsgroup.h"
#include "static.h"
#include "window.h"
#include "font.h"
template <class T>
class DebugInfoNumber : public Window
@@ -34,7 +36,7 @@ class DebugInfoNumber : public Window
prefix(prefix),
suffix(suffix)
{
coord_t prefixSize = getTextWidth(prefix, 0,HEADER_COLOR | FONT(XS));
coord_t prefixSize = getTextWidth(prefix, 0, HEADER_COLOR | FONT(XS));
new StaticText(this, {0, 0, prefixSize, rect.h}, prefix, 0, HEADER_COLOR | FONT(XS));
new DynamicNumber<uint32_t>(this, {prefixSize, 0, rect.w - prefixSize, rect.h}, [] {
return menusStack.available();
+3 -2
View File
@@ -19,6 +19,7 @@
*/
#include "opentx.h"
#include "widget.h"
std::list<const WidgetFactory *> & getRegisteredWidgets()
{
@@ -34,7 +35,7 @@ void registerWidget(const WidgetFactory * factory)
const WidgetFactory * getWidgetFactory(const char * name)
{
std::list<const WidgetFactory *>::const_iterator it = getRegisteredWidgets().cbegin();
auto it = getRegisteredWidgets().cbegin();
for (; it != getRegisteredWidgets().cend();++it) {
if (!strcmp(name, (*it)->getName())) {
return (*it);
@@ -43,7 +44,7 @@ const WidgetFactory * getWidgetFactory(const char * name)
return nullptr;
}
Widget * loadWidget(const char * name, FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData)
Widget * loadWidget(const char * name, FormGroup * parent, const rect_t & rect, WidgetPersistentData * persistentData)
{
const WidgetFactory * factory = getWidgetFactory(name);
if (factory) {
+24 -40
View File
@@ -23,26 +23,24 @@
#include <list>
#include <string.h>
#include "form.h"
#include "zone.h"
#include "widgets_container.h"
#include "debug.h"
#define MAX_WIDGET_OPTIONS 5
// YAML_GENERATOR defs
#if !defined(USE_IDX)
#define USE_IDX
#endif
class WidgetFactory;
class Widget : public FormField
class Widget : public Window
{
public:
struct PersistentData {
ZoneOptionValueTyped options[MAX_WIDGET_OPTIONS] USE_IDX;
};
Widget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, PersistentData * persistentData):
FormField(parent, rect),
typedef WidgetPersistentData PersistentData;
Widget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, WidgetPersistentData * persistentData):
Window(parent, rect),
factory(factory),
persistentData(persistentData)
{
@@ -71,25 +69,30 @@ class Widget : public FormField
return &persistentData->options[index].value;
}
inline void setOptionValue(unsigned int index, const ZoneOptionValue& value)
{
persistentData->options[index].value = value;
}
//
// TODO: for some reason, this one crashes on the radio...
//
// inline void setOptionValue(unsigned int index, const ZoneOptionValue& value)
// {
// persistentData->options[index].value = value;
// }
inline PersistentData * getPersistentData()
{
return persistentData;
}
void paint(BitmapBuffer * dc) override
{
FormField::paint(dc);
}
virtual void background()
{
}
#if defined(DEBUG_WINDOWS)
std::string getName() const override
{
return "Widget";
}
#endif
protected:
const WidgetFactory * factory;
PersistentData * persistentData;
@@ -122,11 +125,11 @@ class WidgetFactory
memset(persistentData, 0, sizeof(Widget::PersistentData));
if (options) {
int i = 0;
for (const ZoneOption * option = options; option->name; option++) {
for (const ZoneOption * option = options; option->name; option++, i++) {
TRACE("WidgetFactory::initPersistentData() setting option '%s'", option->name);
// TODO compiler bug? The CPU freezes ... persistentData->options[i++] = option->deflt;
memcpy(&persistentData->options[i++].value, &option->deflt, sizeof(ZoneOptionValue));
persistentData->options[i++].type = zoneValueEnumFromType(option->type);
memcpy(&persistentData->options[i].value, &option->deflt, sizeof(ZoneOptionValue));
persistentData->options[i].type = zoneValueEnumFromType(option->type);
}
}
}
@@ -138,25 +141,6 @@ class WidgetFactory
const ZoneOption * options;
};
template<class T>
class BaseWidgetFactory: public WidgetFactory
{
public:
BaseWidgetFactory(const char * name, const ZoneOption * options):
WidgetFactory(name, options)
{
}
Widget * create(FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData, bool init = true) const override
{
if (init) {
initPersistentData(persistentData);
}
return new T(this, parent, rect, persistentData);
}
};
inline const ZoneOption * Widget::getOptions() const
{
return getFactory()->getOptions();
+33 -18
View File
@@ -19,7 +19,11 @@
*/
#include "opentx.h"
#include "widget_settings.h"
#include "libopenui.h"
#define SET_DIRTY() storageDirty(EE_MODEL)
static const rect_t widgetSettingsDialogRect = {
LCD_W / 10, // x
@@ -51,50 +55,58 @@ WidgetSettings::WidgetSettings(Window * parent, Widget * widget) :
case ZoneOption::Integer:
new NumberEdit(form, grid.getFieldSlot(), option.min.signedValue, option.max.signedValue,
[=]() -> int {
return widget->getOptionValue(optIdx)->signedValue;
return widget->getOptionValue(optIdx)->signedValue;
},
[=](int32_t newValue) {
widget->setOptionValue(optIdx, OPTION_VALUE_SIGNED(newValue));
widget->getOptionValue(optIdx)->signedValue = newValue;
});
break;
case ZoneOption::Source:
new SourceChoice(form, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM,
[=]() -> int16_t {
return (int16_t) widget->getOptionValue(optIdx)->unsignedValue;
return (int16_t) widget->getOptionValue(optIdx)->unsignedValue;
},
[=](int16_t newValue) {
widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue));
widget->getOptionValue(optIdx)->unsignedValue = (uint32_t)newValue;
});
break;
case ZoneOption::Bool:
new CheckBox(form, grid.getFieldSlot(),
[=]() -> uint8_t {
return (uint8_t) widget->getOptionValue(optIdx)->boolValue;
return (uint8_t) widget->getOptionValue(optIdx)->boolValue;
},
[=](int8_t newValue) {
widget->setOptionValue(optIdx, OPTION_VALUE_BOOL((uint32_t) newValue));
widget->getOptionValue(optIdx)->boolValue = newValue;
});
break;
case ZoneOption::String:
new ModelTextEdit(form, grid.getFieldSlot(), widget->getOptionValue(optIdx)->stringValue, sizeof( widget->getOptionValue(optIdx)->stringValue));
break;
case ZoneOption::File:
break;
case ZoneOption::TextSize:
break;
case ZoneOption::Timer: // Unsigned
{
auto tmChoice = new Choice(form, grid.getFieldSlot(), 0, TIMERS - 1,
case ZoneOption::TextSize: {
new Choice(form, grid.getFieldSlot(), STR_FONT_SIZES, 0, FONTS_COUNT - 1,
[=]() -> int { // getValue
return (int) widget->getOptionValue(optIdx)->unsignedValue;
},
[=](int newValue) { // setValue
widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue));
widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue;
});
break;
}
case ZoneOption::Timer: // Unsigned
{
auto tmChoice = new Choice(form, grid.getFieldSlot(), 0, TIMERS - 1,
[=]() -> int { // getValue
return (int) widget->getOptionValue(optIdx)->unsignedValue;
},
[=](int newValue) { // setValue
widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue;
});
tmChoice->setTextHandler([](int value) {
@@ -108,19 +120,22 @@ WidgetSettings::WidgetSettings(Window * parent, Widget * widget) :
option.min.unsignedValue, // min
option.max.unsignedValue, // max
[=]() -> int16_t { // getValue
return (uint8_t) widget->getOptionValue(optIdx)->unsignedValue;
return (uint8_t) widget->getOptionValue(optIdx)->unsignedValue;
},
[=](int16_t newValue) { // setValue
widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue));
widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue;
});
break;
case ZoneOption::Color:
new ColorEdit(form, grid.getFieldSlot(), [=]() -> int { // getValue
return (int) widget->getOptionValue(optIdx)->unsignedValue;
for (uint8_t part = 0; part < 3; part++)
new ColorEdit(form, grid.getFieldSlot(),
[=]() -> int { // getValue
return (int) widget->getOptionValue(optIdx)->unsignedValue;
},
[=](int newValue) { // setValue
widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue));
widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue;
SET_DIRTY();
});
break;
}
+1
View File
@@ -19,6 +19,7 @@
*/
#include "opentx.h"
#include "widgets_container_impl.h"
class GaugeWidget: public Widget
{
@@ -19,6 +19,8 @@
*/
#include "opentx.h"
#include "widgets_container_impl.h"
#include <memory>
class ModelBitmapWidget: public Widget
@@ -19,12 +19,16 @@
*/
#include "opentx.h"
#include "widgets_container_impl.h"
#define RECT_BORDER 1
#define ROW_HEIGHT 17
#define VIEW_CHANNELS_LIMIT_PCT (g_model.extendedLimits ? LIMIT_EXT_PERCENT : 100)
constexpr uint32_t OUTPUTS_REFRESH = 1000 / 5; // 5 Hz
class OutputsWidget: public Widget
{
public:
@@ -100,11 +104,23 @@ class OutputsWidget: public Widget
void checkEvents() override
{
Widget::checkEvents();
// Last time we refreshed the window
uint32_t now = RTOS_GET_MS();
if (now - lastRefresh >= OUTPUTS_REFRESH) {
lastRefresh = now;
invalidate();
}
invalidate();
}
static const ZoneOption options[];
protected:
// Last time we refreshed the window
uint32_t lastRefresh = 0;
};
const ZoneOption OutputsWidget::options[] = {
+1 -3
View File
@@ -19,6 +19,7 @@
*/
#include "opentx.h"
#include "widgets_container_impl.h"
#define TEXT_WIDGET_DEFAULT_LABEL 'M', 'y', ' ', 'L', 'a', 'b', 'e', 'l' // "My Label"
@@ -32,9 +33,6 @@ class TextWidget: public Widget
void paint(BitmapBuffer * dc) override
{
// clear the background
dc->clear(DEFAULT_BGCOLOR);
// get font color from options[1]
lcdSetColor(persistentData->options[1].value.unsignedValue);
+1
View File
@@ -19,6 +19,7 @@
*/
#include "opentx.h"
#include "widgets_container_impl.h"
class TimerWidget: public Widget
{
+3 -6
View File
@@ -19,6 +19,7 @@
*/
#include "opentx.h"
#include "widgets_container_impl.h"
const coord_t NUMBERS_PADDING = 4;
@@ -32,10 +33,6 @@ class ValueWidget: public Widget
void paint(BitmapBuffer * dc) override
{
// draw the background and border
dc->clear(DEFAULT_BGCOLOR);
dc->drawFilledRect(0,0, width(), height(), SOLID, MAINVIEW_PANES_COLOR | OPACITY(5));
// get source from options[0]
mixsrc_t field = persistentData->options[0].value.unsignedValue;
@@ -50,8 +47,8 @@ class ValueWidget: public Widget
yValue = 14;
xLabel = 0;
yLabel = 0;
attrValue = LEFT | NO_UNIT | FONT(XS);
attrLabel = FONT(XS);
attrValue = LEFT | NO_UNIT | FONT(L);
attrLabel = LEFT;
}
else if (height() < 50) {
xValue = width() - NUMBERS_PADDING;
@@ -0,0 +1,166 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* 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 "widget.h"
#include "widgets_container.h"
template<int N, int O>
class WidgetsContainerImpl: public WidgetsContainer
{
public:
typedef WidgetsContainerPersistentData<N,O> PersistentData;
WidgetsContainerImpl(const rect_t & rect, PersistentData * persistentData):
WidgetsContainer(nullptr, rect, FORM_FORWARD_FOCUS),
persistentData(persistentData)
{
}
Widget * createWidget(unsigned int index, const WidgetFactory * factory) override
{
if (index >= N)
return nullptr;
// remove old one if existing
removeWidget(index);
Widget * widget = nullptr;
if (factory) {
strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(ZonePersistentData::widgetName));
widget = factory->create(this, getZone(index), &persistentData->zones[index].widgetData);
}
widgets[index] = widget;
if (widget)
widget->attach(this);
return widget;
}
void removeWidget(unsigned int index) override
{
if (index >= N)
return;
if (widgets[index])
widgets[index]->deleteLater();
widgets[index] = nullptr;
memset(persistentData->zones[index].widgetName, 0, sizeof(ZonePersistentData::widgetName));
memset(&persistentData->zones[index].widgetData, 0, sizeof(Widget::PersistentData));
}
Widget * getWidget(unsigned int index) override
{
if (index < N)
return widgets[index];
return nullptr;
}
virtual void create()
{
memset(persistentData, 0, sizeof(PersistentData));
}
virtual void load()
{
unsigned int count = getZonesCount();
for (unsigned int i = 0; i < count; i++) {
// remove old widget
if (widgets[i]) {
widgets[i]->deleteLater();
widgets[i] = nullptr;
}
// and load new one if required
if (persistentData->zones[i].widgetName[0]) {
char name[WIDGET_NAME_LEN + 1];
memset(name, 0, sizeof(name));
strncpy(name, persistentData->zones[i].widgetName, WIDGET_NAME_LEN);
widgets[i] = loadWidget(name, this, getZone(i), &persistentData->zones[i].widgetData);
}
}
}
inline ZoneOptionValue * getOptionValue(unsigned int index) const
{
return &persistentData->options[index].value;
}
inline void setOptionValue(unsigned int index, const ZoneOptionValue& value)
{
persistentData->options[index].value = value;
}
unsigned int getZonesCount() const override = 0;
rect_t getZone(unsigned int index) const override = 0;
virtual void background()
{
for (int i = 0; i < N; i++) {
if (widgets[i]) {
widgets[i]->background();
}
}
}
void updateZones()
{
for (int i = 0; i < N; i++) {
if (widgets[i]) {
auto zone = getZone(i);
widgets[i]->setRect(zone);
widgets[i]->setInnerHeight(zone.h);
}
}
}
void adjustLayout() override
{
}
protected:
PersistentData * persistentData;
Widget * widgets[N] = {};
};
template<class T>
class BaseWidgetFactory: public WidgetFactory
{
public:
BaseWidgetFactory(const char * name, const ZoneOption * options):
WidgetFactory(name, options)
{
}
Widget * create(FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData, bool init = true) const override
{
if (init) {
initPersistentData(persistentData);
}
return new T(this, parent, rect, persistentData);
}
};
+46 -95
View File
@@ -22,109 +22,60 @@
#define _WIDGETS_CONTAINER_H_
#include <stdlib.h>
#include <window.h>
#include "widget.h"
#include "libopenui_types.h"
#include "form.h"
#include "zone.h"
class WidgetsContainerInterface
#define WIDGET_NAME_LEN 10
#define MAX_WIDGET_OPTIONS 5 // Name?
#define MAX_TOPBAR_ZONES 4
#define MAX_TOPBAR_OPTIONS 1 // just because of VC++ which doesn't like 0-size arrays :(
// Common 'ZoneOptionValue's among all layouts
enum {
LAYOUT_OPTION_TOPBAR = 0,
LAYOUT_OPTION_FM,
LAYOUT_OPTION_SLIDERS,
LAYOUT_OPTION_TRIMS,
LAYOUT_OPTION_MIRRORED,
LAYOUT_OPTION_LAST_DEFAULT=LAYOUT_OPTION_MIRRORED
};
class Widget;
class WidgetFactory;
class LayoutFactory;
struct WidgetPersistentData {
ZoneOptionValueTyped options[MAX_WIDGET_OPTIONS] USE_IDX;
};
struct ZonePersistentData {
char widgetName[WIDGET_NAME_LEN];
WidgetPersistentData widgetData;
};
template<int N, int O>
struct WidgetsContainerPersistentData {
ZonePersistentData zones[N];
ZoneOptionValueTyped options[O];
};
typedef WidgetsContainerPersistentData<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS> TopBarPersistentData;
class WidgetsContainer: public FormGroup
{
public:
using FormGroup::FormGroup;
virtual unsigned int getZonesCount() const = 0;
virtual rect_t getZone(unsigned int index) const = 0;
virtual Widget * createWidget(unsigned int index, const WidgetFactory * factory) = 0;
virtual Widget * getWidget(unsigned int index) = 0;
virtual void removeWidget(unsigned int index) = 0;
virtual void adjustLayout() = 0;
};
#define WIDGET_NAME_LEN 10
template<int N, int O>
class WidgetsContainer: public FormGroup, public WidgetsContainerInterface
{
public:
struct ZonePersistentData {
char widgetName[WIDGET_NAME_LEN];
Widget::PersistentData widgetData;
};
struct PersistentData {
ZonePersistentData zones[N];
ZoneOptionValueTyped options[O];
};
WidgetsContainer(const rect_t & rect, PersistentData * persistentData):
FormGroup(nullptr, rect, FORM_FORWARD_FOCUS),
persistentData(persistentData)
{
}
Widget * createWidget(unsigned int index, const WidgetFactory * factory) override
{
Widget * widget = nullptr;
memset(persistentData->zones[index].widgetName, 0, sizeof(persistentData->zones[index].widgetName));
if (factory) {
strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(persistentData->zones[index].widgetName));
widget = factory->create(this, getZone(index), &persistentData->zones[index].widgetData);
}
widgets[index] = widget;
return widget;
}
Widget * getWidget(unsigned int index) override
{
if (index < N)
return widgets[index];
return nullptr;
}
virtual void create()
{
memset(persistentData, 0, sizeof(PersistentData));
}
virtual void load()
{
unsigned int count = getZonesCount();
for (unsigned int i = 0; i < count; i++) {
delete widgets[i];
if (persistentData->zones[i].widgetName[0]) {
char name[WIDGET_NAME_LEN + 1];
memset(name, 0, sizeof(name));
strncpy(name, persistentData->zones[i].widgetName, WIDGET_NAME_LEN);
widgets[i] = loadWidget(name, this, getZone(i), &persistentData->zones[i].widgetData);
}
else {
widgets[i] = nullptr;
}
}
}
inline ZoneOptionValue * getOptionValue(unsigned int index) const
{
return &persistentData->options[index].value;
}
inline void setOptionValue(unsigned int index, const ZoneOptionValue& value)
{
persistentData->options[index].value = value;
}
unsigned int getZonesCount() const override = 0;
rect_t getZone(unsigned int index) const override = 0;
virtual void background()
{
for (int i = 0; i < N; i++) {
if (widgets[i]) {
widgets[i]->background();
}
}
}
protected:
PersistentData * persistentData;
Widget * widgets[N] = {};
};
#endif // _WIDGETS_CONTAINER_H_
+4 -4
View File
@@ -13,10 +13,10 @@
#define OPTION_VALUE_BOOL(x) { bool(x) }
#define OPTION_VALUE_STRING(...) { *(ZoneOptionValue *)(const char *) #__VA_ARGS__ }
#else
#define OPTION_VALUE_UNSIGNED(x) { .unsignedValue = (x) }
#define OPTION_VALUE_SIGNED(x) { .signedValue = (x) }
#define OPTION_VALUE_BOOL(x) { .boolValue = (x) }
#define OPTION_VALUE_STRING(...) { .stringValue = { __VA_ARGS__ } }
#define OPTION_VALUE_UNSIGNED(x) ZoneOptionValue{ .unsignedValue = (x) }
#define OPTION_VALUE_SIGNED(x) ZoneOptionValue{ .signedValue = (x) }
#define OPTION_VALUE_BOOL(x) ZoneOptionValue{ .boolValue = (x) }
#define OPTION_VALUE_STRING(...) ZoneOptionValue{ .stringValue = { __VA_ARGS__ } }
#endif
union ZoneOptionValue
+6
View File
@@ -21,6 +21,12 @@
#include "opentx.h"
#include "bootloader_flash.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#else
#include "libopenui/src/libopenui_file.h"
#endif
bool isBootloader(const char * filename)
{
FIL file;
+6 -1
View File
@@ -20,7 +20,12 @@
#include "opentx.h"
#include "frsky_firmware_update.h"
#include "libopenui/src/libopenui_file.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#else
#include "libopenui/src/libopenui_file.h"
#endif
#define PRIM_REQ_POWERUP 0
#define PRIM_REQ_VERSION 1
+6
View File
@@ -25,6 +25,12 @@
#include "stk500.h"
#include "debug.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#else
#include "libopenui/src/libopenui_file.h"
#endif
#define UPDATE_MULTI_EXT_BIN ".bin"
class MultiFirmwareUpdateDriver
+4
View File
@@ -21,6 +21,10 @@
#include "opentx.h"
#include "ff.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
FIL g_oLogFile __DMA;
const char * g_logError = nullptr;
uint8_t logDelay;
+4
View File
@@ -26,6 +26,10 @@
#include "telemetry/frsky.h"
#include "telemetry/multi.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
#if defined(PCBX12S)
#include "lua/lua_exports_x12s.inc" // this line must be after lua headers
#elif defined(RADIO_FAMILY_T16)
+6 -1
View File
@@ -27,7 +27,12 @@
#include "bin_allocator.h"
#include "lua_api.h"
#include "sdcard.h"
#include "libopenui/src/libopenui_file.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#else
#include "libopenui/src/libopenui_file.h"
#endif
extern "C" {
#include <lundump.h>
+1 -1
View File
@@ -21,7 +21,7 @@
#include "opentx.h"
#if defined(LIBOPENUI)
#include "mainwindow.h"
#include "libopenui.h"
#endif
uint8_t currentSpeakerVolume = 255;
+4 -3
View File
@@ -22,9 +22,10 @@
#include <io/frsky_firmware_update.h>
#if defined(LIBOPENUI)
// #include "shutdown_animation.h"
#include "radio_calibration.h"
#include "view_main.h"
#include "libopenui.h"
// #include "shutdown_animation.h"
#include "radio_calibration.h"
#include "view_main.h"
#endif
#if defined(PCBSKY9X)
-4
View File
@@ -27,10 +27,6 @@
#include "opentx_helpers.h"
#include "touch.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
#if defined(SIMU)
#include "targets/simu/simpgmspace.h"
#endif
+6 -1
View File
@@ -21,7 +21,12 @@
#include <stdint.h>
#include "opentx.h"
#include "diskio.h"
#include "libopenui/src/libopenui_file.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#else
#include "libopenui/src/libopenui_file.h"
#endif
bool sdCardFormat()
{
+1 -1
View File
@@ -383,7 +383,7 @@ void OpenTxSim::updateKeysAndSwitches(bool start)
static FXuint trimKeys[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12 };
#endif
for (unsigned i=0; i<NUM_TRIMS_KEYS; i++) {
for (unsigned i=0; i<sizeof(trimKeys)/(2*sizeof(FXuint)); i++) {
simuSetTrim(i, getApp()->getKeyState(trimKeys[i]));
}
@@ -398,11 +398,11 @@ PACK(struct TelemetrySensor_218 {
#if defined(PCBHORUS)
PACK(struct CustomScreenData_v218 {
char LayoutId[10];
Layout::PersistentData layoutData;
LayoutPersistentData layoutData;
});
#define VIEW_DATA \
CustomScreenData screenData[MAX_CUSTOM_SCREENS]; \
TopBar::PersistentData topbarData; \
TopBarPersistentData topbarData; \
uint8_t view;
#elif defined(PCBTARANIS)
#define VIEW_DATA uint8_t view;
+14
View File
@@ -60,6 +60,7 @@ void storageFormat()
sdCheckAndCreateDirectory(RADIO_PATH);
sdCheckAndCreateDirectory(MODELS_PATH);
storageCreateModelsList();
generalDefault();
}
void storageCheck(bool immediately)
@@ -148,6 +149,19 @@ void storageReadAll()
}
}
// Current model filename is empty...
// Let's fix it!
if (strlen(g_eeGeneral.currModelFilename) == 0) {
// sizeof(currModelFilename) == LEN_MODEL_FILENAME + 1
// make sure it is terminated (see doc for strncpy())
strncpy(g_eeGeneral.currModelFilename, DEFAULT_MODEL_FILENAME, LEN_MODEL_FILENAME);
g_eeGeneral.currModelFilename[LEN_MODEL_FILENAME] = '\0';
storageDirty(EE_GENERAL);
storageCheck(true);
}
if (loadModel(g_eeGeneral.currModelFilename, false) != nullptr) {
TRACE("No current model or SD card error");
}
+4
View File
@@ -61,6 +61,10 @@ void preModelLoad()
#endif
stopTrainer();
#if defined(COLORLCD)
deleteCustomScreens();
#endif
}
void postRadioSettingsLoad()
+1 -1
View File
@@ -29,7 +29,7 @@
typedef uint16_t pixel_t;
constexpr bool WRAP_FORM_FIELDS_WITHIN_PAGE = true;
constexpr uint32_t SLIDE_SPEED_REDUCTION = 5;
constexpr short SLIDE_SPEED_REDUCTION = 5;
constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 33;
constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = 47;
+3 -2
View File
@@ -83,8 +83,8 @@ else()
# struct packing breaks on MinGW w/out -mno-ms-bitfields: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 & http://stackoverflow.com/questions/24015852/struct-packing-and-alignment-with-mingw
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-ms-bitfields")
endif()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -fsanitize=address")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
@@ -98,6 +98,7 @@ if(FOX_FOUND)
endif()
add_executable(simu WIN32 ${SIMU_SRC} ${RADIO_SRC_DIR}/simu.cpp)
add_dependencies(simu ${RADIO_DEPENDENCIES})
target_include_directories(simu PUBLIC ${FOX_INCLUDE_DIR} )
target_link_libraries(simu ${FOX_LIBRARY} pthread ${SDL_LIBRARY})
target_compile_definitions(simu PUBLIC -DSIMU)
+4
View File
@@ -20,6 +20,10 @@
#include "opentx.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
void processGetHardwareInfoFrame(uint8_t module, const uint8_t * frame)
{
if (moduleState[module].mode != MODULE_MODE_GET_HARDWARE_INFO) {
+4
View File
@@ -23,6 +23,10 @@
#include "pulses/afhds3.h"
#include "mixer_scheduler.h"
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
uint8_t telemetryStreaming = 0;
uint8_t telemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1)
uint8_t telemetryRxBufferCount = 0;
@@ -22,6 +22,10 @@
#define _USE_MATH_DEFINES
#include <math.h>
#if defined(LIBOPENUI)
#include "libopenui.h"
#endif
TelemetryItem telemetryItems[MAX_TELEMETRY_SENSORS];
uint8_t allowNewSensors;
+3
View File
@@ -22,6 +22,9 @@
#define ISTR(x) const char STR_##x[] = LEN_##x TR_##x
#if defined(COLORLCD)
ISTR(FONT_SIZES);
#endif
ISTR(OFFON);
ISTR(MMMINV);
ISTR(VBEEPMODE);
+1
View File
@@ -152,6 +152,7 @@ extern const char STR_USBMODES[];
extern const char STR_JACK_MODES[];
extern const char STR_VFAILSAFE[];
extern const char STR_FONT_SIZES[];
extern const char STR_VTRAINERMODES[];
extern const char STR_EXTERNAL_MODULE_PROTOCOLS[];
extern const char STR_INTERNAL_MODULE_PROTOCOLS[];
+10
View File
@@ -100,6 +100,16 @@
#define TR_MODULE_R9M_LITE "R9MLite\0 "
#endif
#if defined(COLORLCD)
#if defined(BOLD)
#define LEN_FONT_SIZES "\003"
#define TR_FONT_SIZES "STD"
#else
#define LEN_FONT_SIZES "\004"
#define TR_FONT_SIZES "STD\0""BOLD""XXS\0""XS\0 ""L\0 ""XL\0 ""XXL\0"
#endif
#endif
#define LEN_EXTERNAL_MODULE_PROTOCOLS "\014"
#define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""GHST\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3\0 "