Merge pull request #8442 from opentx/raphaelcoeffic/mainscreens
2.4: main screen improvements
This commit is contained in:
+4
-37
@@ -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
|
||||
|
||||
@@ -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[] =
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "libopenui.h"
|
||||
#endif
|
||||
|
||||
int8_t * curveEnd[MAX_CURVES];
|
||||
|
||||
void loadCurves()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#define _GVAR_NUMBEREDIT_H_
|
||||
|
||||
#include "form.h"
|
||||
#include "choice.h"
|
||||
#include "numberedit.h"
|
||||
#include "gvars.h"
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "curveedit.h"
|
||||
#include "coloredit.h"
|
||||
#include "draw_functions.h"
|
||||
#include "textedits.h"
|
||||
|
||||
inline MessageDialog * createPopupInformation(const char * message)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "radio_trainer.h"
|
||||
#include "opentx.h"
|
||||
#include "libopenui.h"
|
||||
|
||||
#define SET_DIRTY() storageDirty(EE_GENERAL)
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "widgets_container_impl.h"
|
||||
|
||||
class TimerWidget: public Widget
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -21,7 +21,7 @@
|
||||
#include "opentx.h"
|
||||
|
||||
#if defined(LIBOPENUI)
|
||||
#include "mainwindow.h"
|
||||
#include "libopenui.h"
|
||||
#endif
|
||||
|
||||
uint8_t currentSpeakerVolume = 255;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ void preModelLoad()
|
||||
#endif
|
||||
|
||||
stopTrainer();
|
||||
|
||||
#if defined(COLORLCD)
|
||||
deleteCustomScreens();
|
||||
#endif
|
||||
}
|
||||
|
||||
void postRadioSettingsLoad()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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 "
|
||||
|
||||
|
||||
Reference in New Issue
Block a user