newtx/ui/dev_menu.h

346 lines
10 KiB
C
Raw Normal View History

2024-02-02 08:53:17 +00:00
// device liquid crystal display menu
//
// Date: 2024-01-31
//
// Keyboard layout: Four buttons
//
// Enter - to access current selection
//
// Up - to select previous item
//
// Down - to select next item
//
// Stop - to stop scanning right now
#pragma once
#include <base/utils.h>
#include <functional>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <thread>
#include <string.h>
2024-02-02 08:53:17 +00:00
#define MENU_ID_RETURN -1 // ID of menu item that return to parent
#define MENU_ID_BY_TEXT -2
2024-02-02 08:53:17 +00:00
#define LINK_DEFINE(x, y) x##y
#define MEMNU_CMD_HANDLER_PARAM dev_menu* menu, int id, const char* text
#define MENU_CMD_HANDLER_RET bool // whether hold ui
2024-02-02 08:53:17 +00:00
#define MENU_CMD_CALLBACK std::function<MENU_CMD_HANDLER_RET(MEMNU_CMD_HANDLER_PARAM)>
class dev_menu : public refer
{
dev_menu* parent_ = nullptr;
std::string name_ = "";
2024-02-02 08:53:17 +00:00
typedef struct _menu_item
{
std::string text;
bool leaf;
union
{
int id; // valid on leaf
dev_menu *child; // valid on !leaf
};
}MITEM;
std::vector<MITEM> items_;
int init_pos_ = 0;
int cur_ = 0; // focus item
int sel_ = -1; // current checked item
2024-02-04 02:30:09 +00:00
bool check_item_ = false;
bool need_ret_parent_ = true;
2024-02-22 07:20:28 +00:00
std::function<void(dev_menu*)> reset_pos_ = std::function<void(dev_menu*)>();
2024-02-02 08:53:17 +00:00
int find_item(const char* text);
public:
2024-02-04 02:30:09 +00:00
dev_menu(bool check_item = false, bool need_ret_parent = true);
2024-02-02 08:53:17 +00:00
protected:
virtual ~dev_menu();
void set_parent(dev_menu* parent);
public:
bool add_menu(const char* text, int id, int pos = -1);
bool add_menu(const char* text, dev_menu* submenu, int pos = -1);
2024-02-02 08:53:17 +00:00
bool remove_menu(const char* text);
2024-02-04 02:30:09 +00:00
void set_need_return_parent(bool need);
2024-02-02 08:53:17 +00:00
bool move_to(bool next); // true - move to next, false - move to previous. if at end position of move direction, return false
2024-02-22 07:20:28 +00:00
bool select(const char* txt, bool apply_cur = false);
2024-02-02 08:53:17 +00:00
void reset_pos(void);
void set_default_pos(int pos = 0);
2024-02-22 07:20:28 +00:00
void set_default_pos(std::function<void(dev_menu*)> f);
void clear(std::function<void(const char*)> erase_func = std::function<void(const char*)>());
int count(void);
std::string& name(void);
dev_menu* get_sub_menu(const char* text);
dev_menu* get_sub_menu(int index, std::string* title = nullptr);
2024-02-02 08:53:17 +00:00
// Function: access current menu
//
// Parameter: id - to receive ID of current menu item if leaf, fill '-1' if a submenu
//
// text - to receive text of current menu if leaf, empty if submenu
//
2024-02-02 08:53:17 +00:00
// Return: current menu, user should call 'release' after use
dev_menu* enter(int* id, std::string* text);
2024-02-02 08:53:17 +00:00
// Function: get all menus text
//
// Parameter: text - to receive the menu text
//
2024-02-04 02:30:09 +00:00
// sel - to receive current setting index
//
2024-02-02 08:53:17 +00:00
// Return: current menu index
2024-02-04 02:30:09 +00:00
int get_menu_text(std::vector<std::string>& text, int& sel);
2024-02-02 08:53:17 +00:00
};
class KeyMonitor;
class Lcd;
class ui_mgr : public refer
{
dev_menu* root_ = nullptr;
dev_menu* cur_ = nullptr;
2024-02-22 07:20:28 +00:00
const int seq_hour_ = 24;
const int seq_min_ = 84;
2024-02-02 08:53:17 +00:00
volatile bool menu_mode_ = false; // whether LCD is displaying menu
volatile bool run_ = true;
2024-02-05 09:57:39 +00:00
volatile bool ready_enable_ = true;
bool scanning_ = false;
bool stopped_by_ui_ = false; // enable ready message when error occurs in auto-scan
bool int_by_status_ = false;
2024-02-26 09:25:02 +00:00
bool peer_connected_ = false;
int scan_mode_ = 0;
int paper_total_ = 0;
int paper_cnt_ = 0;
2024-02-02 08:53:17 +00:00
SIZE font_size_ = {16, 16};
POINT hold_pos_ = {0, 0};
2024-02-02 08:53:17 +00:00
std::map<int, MENU_CMD_CALLBACK> handler_;
std::map<std::string, MENU_CMD_CALLBACK> handler_s_;
2024-02-02 08:53:17 +00:00
std::unique_ptr<Lcd> lcd_;
std::unique_ptr<KeyMonitor> keyboard_;
class permanent_data : public refer
{
uint64_t history_cnt_ = 0;
uint32_t roller_cnt_ = 0;
uint32_t adden_ = 0;
uint32_t speed_ = 0;
std::string root_;
const std::string his_name_ = "history-count";
const std::string rol_name_ = "roller-count";
#pragma pack(push)
#pragma pack(1)
typedef struct _speed_time
{
uint64_t ms;
uint32_t speed;
}SPEEDTM, *LPSPEEDTM;
typedef struct _times
{
uint64_t history;
uint64_t roller;
}TIMES;
#pragma pack(pop)
std::map<uint32_t, TIMES> speed_times_;
public:
permanent_data()
{
root_ = utils::get_local_data_path() + PATH_SEPARATOR + "record";
utils::create_folder(root_.c_str());
root_ += PATH_SEPARATOR;
safe_file file((root_ + his_name_).c_str());
std::string cont(file.load());
char *ptr = &cont[0];
LPSPEEDTM pspt = nullptr;
if(!cont.empty())
{
history_cnt_ = *(uint64_t*)ptr;
ptr += sizeof(history_cnt_);
pspt = (LPSPEEDTM)ptr;
for(int i = 0; i < (cont.length() - sizeof(history_cnt_)) / sizeof(*pspt); ++i)
{
TIMES t = {0};
t.history = pspt[i].ms;
speed_times_[pspt[i].speed] = t;
}
}
else
{
// test data:
TIMES t = {0};
history_cnt_ = 711;
t.history = 0x65642;
speed_times_[816] = t;
}
file.set_file_path((root_ + rol_name_).c_str());
cont = file.load();
if(!cont.empty())
{
ptr = &cont[0];
roller_cnt_ = *(uint32_t*)ptr;
ptr += sizeof(roller_cnt_);
pspt = (LPSPEEDTM)ptr;
for(int i = 0; i < (cont.length() - sizeof(roller_cnt_)) / sizeof(*pspt); ++i)
{
if(speed_times_.count(pspt[i].speed))
{
speed_times_[pspt[i].speed].roller = pspt[i].ms;
}
else
{
TIMES t = {0};
t.history = t.roller = pspt[i].ms;
speed_times_[pspt[i].speed] = t;
}
}
}
else
{
// test data:
roller_cnt_ = 257;
speed_times_[816].roller = 0x2e54b;
}
}
protected:
virtual ~permanent_data()
{
save();
}
public:
uint64_t history_count(void)
{
return history_cnt_;
}
uint32_t roller_count(void)
{
return roller_cnt_;
}
uint32_t increase_count(uint32_t run_ms, uint64_t* hiscnt = nullptr)
{
++adden_;
++roller_cnt_;
++history_cnt_;
speed_times_[speed_].history += run_ms;
speed_times_[speed_].roller += run_ms;
if(hiscnt)
*hiscnt = history_cnt_;
if(adden_ > 10)
{
save();
adden_ = 0;
}
return roller_cnt_;
}
uint32_t clear_roller_count(void)
{
roller_cnt_ = 0;
for(auto& v: speed_times_)
v.second.roller = 0;
save();
return roller_cnt_;
}
void set_speed(uint32_t speed)
{
speed_ = speed;
if(speed_times_.count(speed) == 0)
{
TIMES t = {0};
speed_times_[speed] = t;
}
}
void save(void)
{
safe_file file((root_ + his_name_).c_str());
std::string cont((char*)&history_cnt_, sizeof(history_cnt_));
for(auto& v: speed_times_)
{
SPEEDTM stm = {0};
stm.speed = v.first;
stm.ms = v.second.history;
cont += std::string((char*)&stm, sizeof(stm));
}
file.save(cont.c_str(), cont.length());
cont = std::string((char*)&roller_cnt_, sizeof(roller_cnt_));
for(auto& v: speed_times_)
{
if(v.second.roller)
{
SPEEDTM stm = {0};
stm.speed = v.first;
stm.ms = v.second.roller;
cont += std::string((char*)&stm, sizeof(stm));
}
}
file.set_file_path((root_ + rol_name_).c_str());
file.save(cont.c_str(), cont.length());
}
};
refer_guard<permanent_data> perm_data_;
void add_menu(void* data);
bool do_menu_command(int cmd, const char* text); // return whether should hold UI ?
bool set_option_value(const char* name, const char* val, size_t size);
bool send_paper_count(void);
2024-02-02 08:53:17 +00:00
void init(void);
void clear(void);
void refresh_lcd(bool cur_at_top);
void move_to(bool next);
void enter(void);
2024-02-19 02:51:17 +00:00
enum dispaly_method
{
DISP_METHOD_CLEAR = 0,
DISP_METHOD_PART_LINE,
DISP_METHOD_WHOLE_LINE,
};
2024-02-02 08:53:17 +00:00
typedef struct _disp_data
{
uint8_t x;
2024-02-19 02:51:17 +00:00
uint8_t y : 6;
uint8_t method : 2;
uint8_t cnt; // clear width when ptr[0] == nullptr
uint8_t mask; // clear height when ptr[0] == nullptr
2024-02-02 08:53:17 +00:00
uint8_t *ptr[16];
}DISPDATA;
2024-02-05 09:57:39 +00:00
MUTEX ready_lck_;
chronograph ready_watch_;
DISPDATA ready_;
safe_fifo<DISPDATA> disp_data_;
safe_thread disp_thrd_;
2024-02-02 08:53:17 +00:00
void thread_display(void);
void display_scan_title(void);
void display_ready(bool time_only);
2024-02-26 09:25:02 +00:00
bool display_status(void* data);
2024-02-20 03:29:26 +00:00
void set_ready_status_enabled(bool enable); // disable ready message, the last message will display until keyboard event triggered
2024-02-05 09:57:39 +00:00
void reset_ready_watch(void);
int get_ready_watch_ms(void);
2024-02-02 08:53:17 +00:00
public:
ui_mgr();
protected:
virtual ~ui_mgr();
public:
void key_event(int key);
};