[color] mixer scripts screen

Todo:
- display only one of .lua/.luac in filechoice
- display proper script status on list page
This commit is contained in:
Raphael Coeffic
2021-07-13 08:40:17 +02:00
parent 38a99bca23
commit a6674ae236
6 changed files with 343 additions and 9 deletions
+1
View File
@@ -76,6 +76,7 @@ endmacro(add_gui_src)
if(LUA)
add_gui_src(standalone_lua.cpp)
add_gui_src(model_mixer_scripts.cpp)
endif()
if(HELI)
+1 -1
View File
@@ -36,7 +36,7 @@
#define GET_SET_DEFAULT(value) GET_DEFAULT(value), SET_DEFAULT(value)
#define GET_SET_INVERTED(value) GET_INVERTED(value), SET_INVERTED(value)
#define GET_SET_VALUE_WITH_OFFSET(value, offset) GET_VALUE_WITH_OFFSET(value, offset), SET_VALUE_WITH_OFFSET(value, offset)
#define GET_SET_WITH_OFFSET(value, offset) GET_VALUE_WITH_OFFSET(value, offset), SET_VALUE_WITH_OFFSET(value, offset)
#define GET_SET_WITH_OFFSET(value, offset) GET_VALUE_WITH_OFFSET(value, offset), SET_VALUE_WITH_OFFSET(value, offset)
#define GET_SET_BF(value, offset, bits) GET_VALUE_WITH_BF(value, offset, bits), SET_VALUE_WITH_BF(value, offset, bits)
#endif // _GETSET_HELPERS_H_
+4
View File
@@ -29,6 +29,7 @@
#include "model_gvars.h"
#include "model_curves.h"
#include "model_logical_switches.h"
#include "model_mixer_scripts.h"
#include "special_functions.h"
#include "model_telemetry.h"
@@ -47,5 +48,8 @@ ModelMenu::ModelMenu():
addTab(new ModelGVarsPage());
addTab(new ModelLogicalSwitchesPage());
addTab(new SpecialFunctionsPage(g_model.customFn));
#if defined(LUA)
addTab(new ModelMixerScriptsPage());
#endif
addTab(new ModelTelemetryPage());
}
@@ -0,0 +1,289 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "model_mixer_scripts.h"
#include "dataconstants.h"
#include "lua/lua_api.h"
#include "translations.h"
#include "menus.h"
#include "libopenui.h"
#include <string.h>
#define SET_DIRTY() storageDirty(EE_MODEL)
class ScriptEditWindow : public Page {
public:
explicit ScriptEditWindow(uint8_t idx) :
Page(ICON_MODEL_LUA_SCRIPTS),
idx(idx)
{
buildBody(&body);
buildHeader(&header);
}
protected:
uint8_t idx;
bool update = false;
void checkEvents() override
{
if (update) {
TRACE("rebuilding ScriptEditWindow...");
rebuildBody(&body);
update = false;
}
// note: 'update' is set from Page::checkEvents()
Page::checkEvents();
}
void buildHeader(Window * window)
{
new StaticText(window,
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT,
PAGE_LINE_HEIGHT},
STR_MENUCUSTOMSCRIPTS, 0, FOCUS_COLOR);
new StaticText(window,
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT,
LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT},
std::string("LUA") + std::to_string(idx + 1), 0, FOCUS_COLOR);
}
void buildBody(FormWindow * window, bool focusScript = false)
{
FormGridLayout grid;
grid.spacer(PAGE_PADDING);
ScriptData* sd = &(g_model.scriptsData[idx]);
// Filename
new StaticText(window, grid.getLabelSlot(), STR_SCRIPT);
auto fc = new FileChoice(
window, grid.getFieldSlot(), SCRIPTS_MIXES_PATH, SCRIPT_EXT,
LEN_SCRIPT_FILENAME,
[=]() { return std::string(sd->file, LEN_SCRIPT_FILENAME); },
[=](std::string newValue) {
strncpy(sd->file, newValue.c_str(), LEN_SCRIPT_FILENAME);
if (newValue.empty()) { memset((void*)sd, 0, sizeof(ScriptData)); }
storageDirty(EE_MODEL);
LUA_LOAD_MODEL_SCRIPT(idx); // async reload...
update = true;
}, true);
grid.nextLine();
// Custom name
new StaticText(window, grid.getLabelSlot(), STR_NAME);
new ModelTextEdit(window, grid.getFieldSlot(), sd->name, sizeof(sd->name));
grid.nextLine();
// scriptInputsOutputs
ScriptInputsOutputs& sio = scriptInputsOutputs[idx];
if (sio.inputsCount > 0) {
new Subtitle(window, grid.getLineSlot(), STR_INPUTS);
grid.nextLine();
auto gInputs =
new FormGroup(window, grid.getFieldSlot(),
FORM_BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST);
GridLayout inputsGrid(gInputs);
for (int i = 0; i < sio.inputsCount; i++) {
ScriptInput& si = sio.inputs[i];
new StaticText(window, grid.getLabelSlot(true), si.name);
grid.nextLine();
if (si.type == INPUT_TYPE_VALUE) {
new NumberEdit(gInputs, inputsGrid.getSlot(), si.min - si.def,
si.max - si.def,
GET_SET_WITH_OFFSET(sd->inputs[i].value, si.def));
} else {
new SourceChoice(gInputs, inputsGrid.getSlot(), 0,
MIXSRC_LAST_TELEM,
GET_SET_DEFAULT(sd->inputs[i].source));
}
inputsGrid.nextLine();
}
gInputs->setHeight(inputsGrid.getWindowHeight());
}
if (sio.outputsCount > 0) {
new Subtitle(window, grid.getLabelSlot(), STR_OUTPUTS);
grid.nextLine();
auto gOutputs =
new FormGroup(window, grid.getLineSlot(),
FORM_BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST);
FormGridLayout outputsGrid(gOutputs->width());
for (int i = 0; i < sio.outputsCount; i++) {
ScriptOutput* so = &(sio.outputs[i]);
new DynamicText(gOutputs, outputsGrid.getLabelSlot(), [=]() {
char s[16];
getSourceString(s,
MIXSRC_FIRST_LUA + (idx * MAX_SCRIPT_OUTPUTS) + i);
return std::string(s, sizeof(s) - 1);
});
new DynamicNumber<int>(gOutputs, outputsGrid.getFieldSlot(),
[=]() { return calcRESXto1000(so->value); });
outputsGrid.nextLine();
}
gOutputs->setHeight(outputsGrid.getWindowHeight());
grid.addWindow(gOutputs);
}
window->setInnerHeight(grid.getWindowHeight());
if (focusScript) { fc->setFocus(); }
}
void rebuildBody(FormWindow * window)
{
coord_t scrollPosition = window->getScrollPositionY();
window->clear();
buildBody(window);
window->setScrollPositionY(scrollPosition);
}
};
class ScriptLineButton : public Button
{
public:
ScriptLineButton(FormGroup* parent, const rect_t& rect,
const ScriptData& scriptData,
const ScriptInternalData* runtimeData) :
Button(parent, rect), scriptData(scriptData), runtimeData(runtimeData)
{
}
void paint(BitmapBuffer* dc) override
{
LcdFlags textColor = DEFAULT_COLOR;
LcdFlags bgColor = FIELD_BGCOLOR;
dc->drawSolidFilledRect(0, 0, width(), height(), bgColor);
if (runtimeData) {
dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP,
scriptData.file, LEN_SCRIPT_FILENAME,
textColor);
}
// bounding rect
if (hasFocus())
dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR);
else
dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR);
}
protected:
const ScriptData& scriptData;
const ScriptInternalData* runtimeData;
};
ModelMixerScriptsPage::ModelMixerScriptsPage() :
PageTab(STR_MENUCUSTOMSCRIPTS, ICON_MODEL_LUA_SCRIPTS)
{
}
void ModelMixerScriptsPage::rebuild(FormWindow * window, int8_t focusIdx)
{
coord_t scrollPosition = window->getScrollPositionY();
window->clear();
build(window, focusIdx);
window->setScrollPositionY(scrollPosition);
}
void ModelMixerScriptsPage::build(FormWindow * window, int8_t focusIdx)
{
FormGridLayout grid;
grid.spacer(PAGE_PADDING);
grid.setLabelWidth(66);
int8_t scriptIdx = 0;
for (int8_t idx = 0; idx < MAX_SCRIPTS; idx++) {
ScriptInternalData* runtimeData = nullptr;
ScriptData &sd = g_model.scriptsData[idx];
if (sd.file[0] != '\0') {
runtimeData = &(scriptInternalData[scriptIdx++]);
}
// LUAx label
auto txt = new StaticText(window, grid.getLabelSlot(),
std::string("LUA") + std::to_string(idx + 1),
BUTTON_BACKGROUND, CENTERED);
Button* button = new ScriptLineButton(window, grid.getFieldSlot(), sd, runtimeData);
button->setPressHandler([=]() -> uint8_t {
Menu* menu = new Menu(window);
menu->addLine(STR_EDIT, [=]() { editLine(window, idx); });
if (runtimeData != nullptr) {
menu->addLine(STR_RESET, [=]() {
memset((void*)&sd, 0, sizeof(sd));
// TODO: anything else? reload scripts???
storageDirty(EE_MODEL);
rebuild(window, idx);
});
return 0;
}
return 0;
});
button->setFocusHandler([=](bool focus) {
if (focus) {
txt->setBackgroundColor(FOCUS_BGCOLOR);
txt->setTextFlags(FOCUS_COLOR | CENTERED);
} else {
txt->setBackgroundColor(FIELD_FRAME_COLOR);
txt->setTextFlags(CENTERED);
}
txt->invalidate();
});
if (focusIdx == idx) {
button->setFocus(SET_FOCUS_DEFAULT);
txt->setBackgroundColor(FOCUS_BGCOLOR);
txt->setTextFlags(FOCUS_COLOR | CENTERED);
txt->invalidate();
}
txt->setHeight(button->height());
grid.spacer(button->height() + 5);
}
grid.nextLine();
window->setInnerHeight(grid.getWindowHeight());
}
void ModelMixerScriptsPage::editLine(FormWindow * window, uint8_t idx)
{
Window::clearFocus();
Window * editWindow = new ScriptEditWindow(idx);
editWindow->setCloseHandler([=]() {
rebuild(window, idx);
});
}
@@ -0,0 +1,39 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "tabsgroup.h"
class ModelMixerScriptsPage: public PageTab {
public:
ModelMixerScriptsPage();
virtual void build(FormWindow * window) override
{
build(window, 0);
}
protected:
void build(FormWindow * window, int8_t focusIdx);
void rebuild(FormWindow * window, int8_t focusIdx);
void editLine(FormWindow * window, uint8_t idx);
};
+9 -8
View File
@@ -1035,15 +1035,16 @@ void ModelSetupPage::build(FormWindow * window)
// Model name
new StaticText(window, grid.getLabelSlot(), STR_MODELNAME);
auto text = new RadioTextEdit(window, grid.getFieldSlot(), g_model.header.name, sizeof(g_model.header.name),
0, MODEL_NAME_EXTRA_CHARS);
auto text =
new ModelTextEdit(window, grid.getFieldSlot(), g_model.header.name,
sizeof(g_model.header.name), 0, MODEL_NAME_EXTRA_CHARS);
text->setChangeHandler([=] {
modelslist.load();
auto model = modelslist.getCurrentModel();
if (model) {
model->setModelName(g_model.header.name);
}
SET_DIRTY();
modelslist.load();
auto model = modelslist.getCurrentModel();
if (model) {
model->setModelName(g_model.header.name);
}
SET_DIRTY();
});
grid.nextLine();