mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
[FL-3456] Allow for larger Infrared remotes (#3164)
* Do not load all signals at once (Draft) * Minor cleanup * Refactor remote renaming * Improve function signatures * Rename infrared_remote functions * Optimise signal loading * Implement adding signals to remote * Add read_name() method * Deprecate a function * Partially implement deleting signals (draft) * Use m-array instead of m-list for signal name directory * Use plain C strings instead of furi_string * Implement deleting signals * Implement deleting signals via generalised callback * Implement renaming signals * Rename some types * Some more renaming * Remove unused type * Implement inserting signals (internal use) * Improve InfraredMoveView * Send an event to move a signal * Remove unused type * Implement moving signals * Implement creating new remotes with one signal * Un-deprecate and rename a function * Add InfraredRemote API docs * Add InfraredSignal API docs * Better error messages * Show progress pop-up when moving buttons in a remote * Copy labels to the InfraredMoveView to avoid pointer invalidation * Improve file selection scene * Show progress pop-up when renaming buttons in a remote * Refactor a scene * Show progress when deleting a button from remote * Use a random name for temp files * Add docs to infrared_brute_force.h * Rename Infrared type to InfraredApp * Add docs to infrared_app_i.h Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
#include "infrared_move_view.h"
|
||||
|
||||
#include <m-array.h>
|
||||
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <toolbox/m_cstr_dup.h>
|
||||
|
||||
#define LIST_ITEMS 4U
|
||||
#define LIST_LINE_H 13U
|
||||
@@ -13,42 +14,41 @@
|
||||
|
||||
struct InfraredMoveView {
|
||||
View* view;
|
||||
InfraredMoveCallback move_cb;
|
||||
void* cb_context;
|
||||
InfraredMoveCallback callback;
|
||||
void* callback_context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char** btn_names;
|
||||
uint32_t btn_number;
|
||||
int32_t list_offset;
|
||||
int32_t item_idx;
|
||||
bool is_moving;
|
||||
ARRAY_DEF(InfraredMoveViewItemArray, const char*, M_CSTR_DUP_OPLIST); //-V575
|
||||
|
||||
InfraredMoveGetItemCallback get_item_cb;
|
||||
typedef struct {
|
||||
InfraredMoveViewItemArray_t labels;
|
||||
int32_t list_offset;
|
||||
int32_t current_idx;
|
||||
int32_t start_idx;
|
||||
bool is_moving;
|
||||
} InfraredMoveViewModel;
|
||||
|
||||
static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
InfraredMoveViewModel* model = _model;
|
||||
|
||||
UNUSED(model);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move");
|
||||
|
||||
bool show_scrollbar = model->btn_number > LIST_ITEMS;
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const bool show_scrollbar = btn_number > LIST_ITEMS;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
for(uint32_t i = 0; i < MIN(model->btn_number, LIST_ITEMS); i++) {
|
||||
int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->btn_number, 0u);
|
||||
uint8_t x_offset = (model->is_moving && model->item_idx == idx) ? MOVE_X_OFFSET : 0;
|
||||
for(uint32_t i = 0; i < MIN(btn_number, LIST_ITEMS); i++) {
|
||||
int32_t idx = CLAMP((uint32_t)(i + model->list_offset), btn_number, 0U);
|
||||
uint8_t x_offset = (model->is_moving && model->current_idx == idx) ? MOVE_X_OFFSET : 0;
|
||||
uint8_t y_offset = HEADER_H + i * LIST_LINE_H;
|
||||
uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
if(model->item_idx == idx) {
|
||||
if(model->current_idx == idx) {
|
||||
canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
@@ -60,7 +60,12 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1);
|
||||
}
|
||||
canvas_draw_str_aligned(
|
||||
canvas, x_offset + 3, y_offset + 3, AlignLeft, AlignTop, model->btn_names[idx]);
|
||||
canvas,
|
||||
x_offset + 3,
|
||||
y_offset + 3,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
*InfraredMoveViewItemArray_cget(model->labels, idx));
|
||||
}
|
||||
|
||||
if(show_scrollbar) {
|
||||
@@ -69,22 +74,22 @@ static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_width(canvas),
|
||||
HEADER_H,
|
||||
canvas_height(canvas) - HEADER_H,
|
||||
model->item_idx,
|
||||
model->btn_number);
|
||||
model->current_idx,
|
||||
btn_number);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_list_offset(InfraredMoveViewModel* model) {
|
||||
int32_t bounds = model->btn_number > (LIST_ITEMS - 1) ? 2 : model->btn_number;
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const int32_t bounds = btn_number > (LIST_ITEMS - 1) ? 2 : btn_number;
|
||||
|
||||
if((model->btn_number > (LIST_ITEMS - 1)) &&
|
||||
(model->item_idx >= ((int32_t)model->btn_number - 1))) {
|
||||
model->list_offset = model->item_idx - (LIST_ITEMS - 1);
|
||||
} else if(model->list_offset < model->item_idx - bounds) {
|
||||
model->list_offset = CLAMP(
|
||||
model->item_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)model->btn_number - bounds, 0);
|
||||
} else if(model->list_offset > model->item_idx - bounds) {
|
||||
model->list_offset = CLAMP(model->item_idx - 1, (int32_t)model->btn_number - bounds, 0);
|
||||
if((btn_number > (LIST_ITEMS - 1)) && (model->current_idx >= ((int32_t)btn_number - 1))) {
|
||||
model->list_offset = model->current_idx - (LIST_ITEMS - 1);
|
||||
} else if(model->list_offset < model->current_idx - bounds) {
|
||||
model->list_offset =
|
||||
CLAMP(model->current_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)btn_number - bounds, 0);
|
||||
} else if(model->list_offset > model->current_idx - bounds) {
|
||||
model->list_offset = CLAMP(model->current_idx - 1, (int32_t)btn_number - bounds, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,117 +100,130 @@ static bool infrared_move_view_input_callback(InputEvent* event, void* context)
|
||||
|
||||
if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) &&
|
||||
((event->key == InputKeyUp) || (event->key == InputKeyDown))) {
|
||||
bool is_moving = false;
|
||||
uint32_t index_old = 0;
|
||||
uint32_t index_new = 0;
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
is_moving = model->is_moving;
|
||||
index_old = model->item_idx;
|
||||
const size_t btn_number = InfraredMoveViewItemArray_size(model->labels);
|
||||
const int32_t item_idx_prev = model->current_idx;
|
||||
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->item_idx <= 0) {
|
||||
model->item_idx = model->btn_number;
|
||||
if(model->current_idx <= 0) {
|
||||
model->current_idx = btn_number;
|
||||
}
|
||||
model->item_idx--;
|
||||
model->current_idx--;
|
||||
|
||||
} else if(event->key == InputKeyDown) {
|
||||
model->item_idx++;
|
||||
if(model->item_idx >= (int32_t)(model->btn_number)) {
|
||||
model->item_idx = 0;
|
||||
model->current_idx++;
|
||||
if(model->current_idx >= (int32_t)(btn_number)) {
|
||||
model->current_idx = 0;
|
||||
}
|
||||
}
|
||||
index_new = model->item_idx;
|
||||
|
||||
if(model->is_moving) {
|
||||
InfraredMoveViewItemArray_swap_at(
|
||||
model->labels, item_idx_prev, model->current_idx);
|
||||
}
|
||||
|
||||
update_list_offset(model);
|
||||
},
|
||||
!is_moving);
|
||||
if((is_moving) && (move_view->move_cb)) {
|
||||
move_view->move_cb(index_old, index_new, move_view->cb_context);
|
||||
infrared_move_view_list_update(move_view);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
true);
|
||||
|
||||
if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {
|
||||
consumed = true;
|
||||
|
||||
} else if((event->key == InputKeyOk) && (event->type == InputTypeShort)) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ model->is_moving = !(model->is_moving); },
|
||||
{
|
||||
if(!model->is_moving) {
|
||||
model->start_idx = model->current_idx;
|
||||
} else if(move_view->callback) {
|
||||
move_view->callback(
|
||||
model->start_idx, model->current_idx, move_view->callback_context);
|
||||
}
|
||||
model->is_moving = !(model->is_moving);
|
||||
},
|
||||
true);
|
||||
|
||||
consumed = true;
|
||||
|
||||
} else if(event->key == InputKeyBack) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
if(model->is_moving && move_view->callback) {
|
||||
move_view->callback(
|
||||
model->start_idx, model->current_idx, move_view->callback_context);
|
||||
}
|
||||
model->is_moving = false;
|
||||
},
|
||||
false);
|
||||
|
||||
// Not consuming, Back event is passed thru
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void infrared_move_view_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
InfraredMoveView* move_view = context;
|
||||
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
if(model->btn_names) {
|
||||
free(model->btn_names);
|
||||
model->btn_names = NULL;
|
||||
}
|
||||
model->btn_number = 0;
|
||||
model->get_item_cb = NULL;
|
||||
},
|
||||
false);
|
||||
move_view->cb_context = NULL;
|
||||
}
|
||||
|
||||
void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback) {
|
||||
furi_assert(move_view);
|
||||
move_view->move_cb = callback;
|
||||
}
|
||||
|
||||
void infrared_move_view_list_init(
|
||||
void infrared_move_view_set_callback(
|
||||
InfraredMoveView* move_view,
|
||||
uint32_t item_count,
|
||||
InfraredMoveGetItemCallback load_cb,
|
||||
InfraredMoveCallback callback,
|
||||
void* context) {
|
||||
furi_assert(move_view);
|
||||
move_view->cb_context = context;
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
furi_assert(model->btn_names == NULL);
|
||||
model->btn_names = malloc(sizeof(char*) * item_count);
|
||||
model->btn_number = item_count;
|
||||
model->get_item_cb = load_cb;
|
||||
},
|
||||
false);
|
||||
move_view->callback = callback;
|
||||
move_view->callback_context = context;
|
||||
}
|
||||
|
||||
void infrared_move_view_list_update(InfraredMoveView* move_view) {
|
||||
furi_assert(move_view);
|
||||
void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ InfraredMoveViewItemArray_push_back(model->labels, label); },
|
||||
true);
|
||||
}
|
||||
|
||||
void infrared_move_view_reset(InfraredMoveView* move_view) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{
|
||||
for(uint32_t i = 0; i < model->btn_number; i++) {
|
||||
if(!model->get_item_cb) break;
|
||||
model->btn_names[i] = model->get_item_cb(i, move_view->cb_context);
|
||||
}
|
||||
InfraredMoveViewItemArray_reset(model->labels);
|
||||
model->list_offset = 0;
|
||||
model->start_idx = 0;
|
||||
model->current_idx = 0;
|
||||
model->is_moving = false;
|
||||
},
|
||||
true);
|
||||
false);
|
||||
move_view->callback_context = NULL;
|
||||
}
|
||||
|
||||
InfraredMoveView* infrared_move_view_alloc(void) {
|
||||
InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView));
|
||||
|
||||
move_view->view = view_alloc();
|
||||
view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel));
|
||||
view_set_draw_callback(move_view->view, infrared_move_view_draw_callback);
|
||||
view_set_input_callback(move_view->view, infrared_move_view_input_callback);
|
||||
view_set_exit_callback(move_view->view, infrared_move_view_on_exit);
|
||||
view_set_context(move_view->view, move_view);
|
||||
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ InfraredMoveViewItemArray_init(model->labels); },
|
||||
true);
|
||||
|
||||
return move_view;
|
||||
}
|
||||
|
||||
void infrared_move_view_free(InfraredMoveView* move_view) {
|
||||
with_view_model(
|
||||
move_view->view,
|
||||
InfraredMoveViewModel * model,
|
||||
{ InfraredMoveViewItemArray_clear(model->labels); },
|
||||
true);
|
||||
|
||||
view_free(move_view->view);
|
||||
free(move_view);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user