My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

menus.cpp 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * Menu functions for ProUI
  24. * Author: Miguel A. Risco-Castillo
  25. * Version: 1.5.1
  26. * Date: 2022/05/23
  27. *
  28. * This program is free software: you can redistribute it and/or modify
  29. * it under the terms of the GNU Lesser General Public License as
  30. * published by the Free Software Foundation, either version 3 of the License, or
  31. * (at your option) any later version.
  32. *
  33. * This program is distributed in the hope that it will be useful,
  34. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  35. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  36. * GNU General Public License for more details.
  37. *
  38. * You should have received a copy of the GNU Lesser General Public License
  39. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  40. *
  41. */
  42. #include "../../../inc/MarlinConfigPre.h"
  43. #if ENABLED(DWIN_LCD_PROUI)
  44. #include "../common/encoder.h"
  45. #include "dwin_lcd.h"
  46. #include "dwinui.h"
  47. #include "dwin.h"
  48. #include "menus.h"
  49. int8_t MenuItemTotal = 0;
  50. int8_t MenuItemCount = 0;
  51. MenuItemClass** MenuItems = nullptr;
  52. MenuClass *CurrentMenu = nullptr;
  53. MenuClass *PreviousMenu = nullptr;
  54. void (*onMenuDraw)(MenuClass* menu) = nullptr;
  55. void (*onCursorErase)(const int8_t line) = nullptr;
  56. void (*onCursorDraw)(const int8_t line) = nullptr;
  57. MenuData_t MenuData;
  58. // Menuitem Drawing functions =================================================
  59. void Draw_Title(TitleClass* title) {
  60. DWIN_Draw_Rectangle(1, HMI_data.TitleBg_color, 0, 0, DWIN_WIDTH - 1, TITLE_HEIGHT - 1);
  61. if (title->frameid)
  62. DWIN_Frame_AreaCopy(title->frameid, title->frame.left, title->frame.top, title->frame.right, title->frame.bottom, 14, (TITLE_HEIGHT - (title->frame.bottom - title->frame.top)) / 2 - 1);
  63. else
  64. DWIN_Draw_String(false, DWIN_FONT_HEAD, HMI_data.TitleTxt_color, HMI_data.TitleBg_color, 14, (TITLE_HEIGHT - DWINUI::fontHeight(DWIN_FONT_HEAD)) / 2 - 1, title->caption);
  65. }
  66. void Draw_Menu(MenuClass* menu) {
  67. DWINUI::SetColors(HMI_data.Text_Color, HMI_data.Background_Color, HMI_data.StatusBg_Color);
  68. DWIN_Draw_Rectangle(1, DWINUI::backcolor, 0, TITLE_HEIGHT, DWIN_WIDTH - 1, STATUS_Y - 1);
  69. }
  70. void Draw_Menu_Cursor(const int8_t line) {
  71. const uint16_t ypos = MYPOS(line);
  72. DWINUI::Draw_Box(1, HMI_data.Cursor_color, {0, ypos, 15, MLINE - 1});
  73. }
  74. void Erase_Menu_Cursor(const int8_t line) {
  75. const uint16_t ypos = MYPOS(line);
  76. DWINUI::Draw_Box(1, HMI_data.Background_Color, {0, ypos, 15, MLINE - 1});
  77. }
  78. void Draw_Menu_Line(const uint8_t line, const uint8_t icon /*=0*/, const char * const label /*=nullptr*/, bool more /*=false*/) {
  79. if (icon) DWINUI::Draw_Icon(icon, ICOX, MBASE(line) - 3);
  80. if (label) DWINUI::Draw_String(LBLX, MBASE(line) - 1, (char*)label);
  81. if (more) DWINUI::Draw_Icon(ICON_More, VALX + 16, MBASE(line) - 3);
  82. DWIN_Draw_HLine(HMI_data.SplitLine_Color, 16, MYPOS(line + 1), 240);
  83. }
  84. void Draw_Chkb_Line(const uint8_t line, const bool checked) {
  85. DWINUI::Draw_Checkbox(HMI_data.Text_Color, HMI_data.Background_Color, VALX + 3 * DWINUI::fontWidth(), MBASE(line) - 1, checked);
  86. }
  87. void Draw_Menu_IntValue(uint16_t bcolor, const uint8_t line, uint8_t iNum, const int32_t value /*=0*/) {
  88. DWINUI::Draw_Signed_Int(HMI_data.Text_Color, bcolor, iNum , VALX, MBASE(line) - 1, value);
  89. }
  90. void onDrawMenuItem(MenuItemClass* menuitem, int8_t line) {
  91. if (menuitem->icon) DWINUI::Draw_Icon(menuitem->icon, ICOX, MBASE(line) - 3);
  92. if (menuitem->frameid)
  93. DWIN_Frame_AreaCopy(menuitem->frameid, menuitem->frame.left, menuitem->frame.top, menuitem->frame.right, menuitem->frame.bottom, LBLX, MBASE(line));
  94. else if (menuitem->caption)
  95. DWINUI::Draw_String(LBLX, MBASE(line) - 1, menuitem->caption);
  96. DWIN_Draw_HLine(HMI_data.SplitLine_Color, 16, MYPOS(line + 1), 240);
  97. }
  98. void onDrawSubMenu(MenuItemClass* menuitem, int8_t line) {
  99. onDrawMenuItem(menuitem, line);
  100. DWINUI::Draw_Icon(ICON_More, VALX + 16, MBASE(line) - 3);
  101. }
  102. void onDrawIntMenu(MenuItemClass* menuitem, int8_t line, int32_t value) {
  103. onDrawMenuItem(menuitem, line);
  104. Draw_Menu_IntValue(HMI_data.Background_Color, line, 4, value);
  105. }
  106. void onDrawPIntMenu(MenuItemClass* menuitem, int8_t line) {
  107. const int16_t value = *(int16_t*)static_cast<MenuItemPtrClass*>(menuitem)->value;
  108. onDrawIntMenu(menuitem, line, value);
  109. }
  110. void onDrawPInt8Menu(MenuItemClass* menuitem, int8_t line) {
  111. const uint8_t value = *(uint8_t*)static_cast<MenuItemPtrClass*>(menuitem)->value;
  112. onDrawIntMenu(menuitem, line, value);
  113. }
  114. void onDrawPInt32Menu(MenuItemClass* menuitem, int8_t line) {
  115. const uint32_t value = *(uint32_t*)static_cast<MenuItemPtrClass*>(menuitem)->value;
  116. onDrawIntMenu(menuitem, line, value);
  117. }
  118. void onDrawFloatMenu(MenuItemClass* menuitem, int8_t line, uint8_t dp, const float value) {
  119. onDrawMenuItem(menuitem, line);
  120. DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Background_Color, 3, dp, VALX - dp * DWINUI::fontWidth(DWIN_FONT_MENU), MBASE(line), value);
  121. }
  122. void onDrawPFloatMenu(MenuItemClass* menuitem, int8_t line) {
  123. const float value = *(float*)static_cast<MenuItemPtrClass*>(menuitem)->value;
  124. const int8_t dp = UNITFDIGITS;
  125. onDrawFloatMenu(menuitem, line, dp, value);
  126. }
  127. void onDrawPFloat2Menu(MenuItemClass* menuitem, int8_t line) {
  128. const float value = *(float*)static_cast<MenuItemPtrClass*>(menuitem)->value;
  129. onDrawFloatMenu(menuitem, line, 2, value);
  130. }
  131. void onDrawPFloat3Menu(MenuItemClass* menuitem, int8_t line) {
  132. const float value = *(float*)static_cast<MenuItemPtrClass*>(menuitem)->value;
  133. onDrawFloatMenu(menuitem, line, 3, value);
  134. }
  135. void onDrawChkbMenu(MenuItemClass* menuitem, int8_t line, bool checked) {
  136. onDrawMenuItem(menuitem, line);
  137. Draw_Chkb_Line(line, checked);
  138. }
  139. void onDrawChkbMenu(MenuItemClass* menuitem, int8_t line) {
  140. const bool val = *(bool*)static_cast<MenuItemPtrClass*>(menuitem)->value;
  141. onDrawChkbMenu(menuitem, line, val);
  142. }
  143. void DrawItemEdit() {
  144. switch (checkkey) {
  145. case SetIntNoDraw: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  146. case SetInt:
  147. case SetPInt: DWINUI::Draw_Signed_Int(HMI_data.Text_Color, HMI_data.Selected_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, MenuData.Value); break;
  148. case SetFloat:
  149. case SetPFloat: DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Selected_Color, 3, MenuData.dp, VALX - MenuData.dp * DWINUI::fontWidth(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), MenuData.Value / POW(10, MenuData.dp)); break;
  150. default: break;
  151. }
  152. }
  153. //-----------------------------------------------------------------------------
  154. // On click functions
  155. //-----------------------------------------------------------------------------
  156. // Generic onclick event without draw
  157. // process: process id HMI destiny
  158. // lo: low limit
  159. // hi: high limit
  160. // dp: decimal places, 0 for integers
  161. // val: value / scaled value
  162. // LiveUpdate: live update function when the encoder changes
  163. // Apply: update function when the encoder is pressed
  164. void SetOnClick(uint8_t process, const int32_t lo, const int32_t hi, uint8_t dp, const int32_t val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  165. checkkey = process;
  166. MenuData.MinValue = lo;
  167. MenuData.MaxValue = hi;
  168. MenuData.dp = dp;
  169. MenuData.Apply = Apply;
  170. MenuData.LiveUpdate = LiveUpdate;
  171. MenuData.Value = constrain(val, lo, hi);
  172. EncoderRate.enabled = true;
  173. }
  174. // Generic onclick event for integer values
  175. // process: process id HMI destiny
  176. // lo: scaled low limit
  177. // hi: scaled high limit
  178. // val: value
  179. // LiveUpdate: live update function when the encoder changes
  180. // Apply: update function when the encoder is pressed
  181. void SetValueOnClick(uint8_t process, const int32_t lo, const int32_t hi, const int32_t val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  182. SetOnClick(process, lo, hi, 0, val, Apply, LiveUpdate);
  183. Draw_Menu_IntValue(HMI_data.Selected_Color, CurrentMenu->line(), 4, MenuData.Value);
  184. }
  185. // Generic onclick event for float values
  186. // process: process id HMI destiny
  187. // lo: scaled low limit
  188. // hi: scaled high limit
  189. // val: value
  190. // LiveUpdate: live update function when the encoder changes
  191. // Apply: update function when the encoder is pressed
  192. void SetValueOnClick(uint8_t process, const float lo, const float hi, uint8_t dp, const float val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  193. const int32_t value = round(val * POW(10, dp));
  194. SetOnClick(process, lo * POW(10, dp), hi * POW(10, dp), dp, value, Apply, LiveUpdate);
  195. DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Selected_Color, 3, dp, VALX - dp * DWINUI::fontWidth(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), val);
  196. }
  197. // Generic onclick event for integer values
  198. // lo: scaled low limit
  199. // hi: scaled high limit
  200. // val: value
  201. // LiveUpdate: live update function when the encoder changes
  202. // Apply: update function when the encoder is pressed
  203. void SetIntOnClick(const int32_t lo, const int32_t hi, const int32_t val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  204. SetValueOnClick(SetInt, lo, hi, val, Apply, LiveUpdate);
  205. }
  206. // Generic onclick event for set pointer to 16 bit uinteger values
  207. // lo: low limit
  208. // hi: high limit
  209. // LiveUpdate: live update function when the encoder changes
  210. // Apply: update function when the encoder is pressed
  211. void SetPIntOnClick(const int32_t lo, const int32_t hi, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  212. MenuData.P_Int = (int16_t*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
  213. const int32_t value = *MenuData.P_Int;
  214. SetValueOnClick(SetPInt, lo, hi, value, Apply, LiveUpdate);
  215. }
  216. // Generic onclick event for float values
  217. // process: process id HMI destiny
  218. // lo: low limit
  219. // hi: high limit
  220. // dp: decimal places
  221. // val: value
  222. void SetFloatOnClick(const float lo, const float hi, uint8_t dp, const float val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  223. SetValueOnClick(SetFloat, lo, hi, dp, val, Apply, LiveUpdate);
  224. }
  225. // Generic onclick event for set pointer to float values
  226. // lo: low limit
  227. // hi: high limit
  228. // LiveUpdate: live update function when the encoder changes
  229. // Apply: update function when the encoder is pressed
  230. void SetPFloatOnClick(const float lo, const float hi, uint8_t dp, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  231. MenuData.P_Float = (float*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
  232. SetValueOnClick(SetPFloat, lo, hi, dp, *MenuData.P_Float, Apply, LiveUpdate);
  233. }
  234. // HMI Control functions ======================================================
  235. // Generic menu control using the encoder
  236. void HMI_Menu() {
  237. EncoderState encoder_diffState = get_encoder_state();
  238. if (encoder_diffState == ENCODER_DIFF_NO) return;
  239. if (CurrentMenu) {
  240. if (encoder_diffState == ENCODER_DIFF_ENTER)
  241. CurrentMenu->onClick();
  242. else
  243. CurrentMenu->onScroll(encoder_diffState == ENCODER_DIFF_CW);
  244. }
  245. }
  246. // Get an integer value using the encoder without draw anything
  247. // lo: low limit
  248. // hi: high limit
  249. // Return value:
  250. // 0 : no change
  251. // 1 : live change
  252. // 2 : apply change
  253. int8_t HMI_GetIntNoDraw(const int32_t lo, const int32_t hi) {
  254. const int32_t cval = MenuData.Value;
  255. EncoderState encoder_diffState = Encoder_ReceiveAnalyze();
  256. if (encoder_diffState != ENCODER_DIFF_NO) {
  257. if (Apply_Encoder(encoder_diffState, MenuData.Value)) {
  258. EncoderRate.enabled = false;
  259. checkkey = Menu;
  260. return 2;
  261. }
  262. LIMIT(MenuData.Value, lo, hi);
  263. }
  264. return int8_t(cval != MenuData.Value);
  265. }
  266. // Get an integer value using the encoder
  267. // lo: low limit
  268. // hi: high limit
  269. // Return value:
  270. // 0 : no change
  271. // 1 : live change
  272. // 2 : apply change
  273. int8_t HMI_GetInt(const int32_t lo, const int32_t hi) {
  274. EncoderState encoder_diffState = Encoder_ReceiveAnalyze();
  275. if (encoder_diffState != ENCODER_DIFF_NO) {
  276. if (Apply_Encoder(encoder_diffState, MenuData.Value)) {
  277. EncoderRate.enabled = false;
  278. DWINUI::Draw_Signed_Int(HMI_data.Text_Color, HMI_data.Background_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, MenuData.Value);
  279. checkkey = Menu;
  280. return 2;
  281. }
  282. LIMIT(MenuData.Value, lo, hi);
  283. DrawItemEdit();
  284. return 1;
  285. }
  286. return 0;
  287. }
  288. // Set an integer using the encoder
  289. void HMI_SetInt() {
  290. int8_t val = HMI_GetInt(MenuData.MinValue, MenuData.MaxValue);
  291. switch (val) {
  292. case 0: return; break;
  293. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  294. case 2: if (MenuData.Apply) MenuData.Apply(); break;
  295. }
  296. }
  297. // Set an integer without drawing
  298. void HMI_SetIntNoDraw() {
  299. int8_t val = HMI_GetIntNoDraw(MenuData.MinValue, MenuData.MaxValue);
  300. switch (val) {
  301. case 0: return; break;
  302. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  303. case 2: if (MenuData.Apply) MenuData.Apply(); break;
  304. }
  305. }
  306. // Set an integer pointer variable using the encoder
  307. void HMI_SetPInt() {
  308. int8_t val = HMI_GetInt(MenuData.MinValue, MenuData.MaxValue);
  309. switch (val) {
  310. case 0: return;
  311. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  312. case 2: *MenuData.P_Int = MenuData.Value; if (MenuData.Apply) MenuData.Apply(); break;
  313. }
  314. }
  315. // Get a scaled float value using the encoder
  316. // dp: decimal places
  317. // lo: scaled low limit
  318. // hi: scaled high limit
  319. // Return value:
  320. // 0 : no change
  321. // 1 : live change
  322. // 2 : apply change
  323. int8_t HMI_GetFloat(uint8_t dp, int32_t lo, int32_t hi) {
  324. EncoderState encoder_diffState = Encoder_ReceiveAnalyze();
  325. if (encoder_diffState != ENCODER_DIFF_NO) {
  326. if (Apply_Encoder(encoder_diffState, MenuData.Value)) {
  327. EncoderRate.enabled = false;
  328. DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Background_Color, 3, dp, VALX - dp * DWINUI::fontWidth(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), MenuData.Value / POW(10, dp));
  329. checkkey = Menu;
  330. return 2;
  331. }
  332. LIMIT(MenuData.Value, lo, hi);
  333. DrawItemEdit();
  334. return 1;
  335. }
  336. return 0;
  337. }
  338. // Set a scaled float using the encoder
  339. void HMI_SetFloat() {
  340. const int8_t val = HMI_GetFloat(MenuData.dp, MenuData.MinValue, MenuData.MaxValue);
  341. switch (val) {
  342. case 0: return;
  343. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  344. case 2: if (MenuData.Apply) MenuData.Apply(); break;
  345. }
  346. }
  347. // Set a scaled float pointer variable using the encoder
  348. void HMI_SetPFloat() {
  349. const int8_t val = HMI_GetFloat(MenuData.dp, MenuData.MinValue, MenuData.MaxValue);
  350. switch (val) {
  351. case 0: return;
  352. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  353. case 2: *MenuData.P_Float = MenuData.Value / POW(10, MenuData.dp); if (MenuData.Apply) MenuData.Apply(); break;
  354. }
  355. }
  356. // Menu Classes ===============================================================
  357. MenuClass::MenuClass() {
  358. selected = 0;
  359. topline = 0;
  360. }
  361. void MenuClass::draw() {
  362. MenuTitle.draw();
  363. if (onMenuDraw != nullptr) onMenuDraw(this);
  364. for (int8_t i = 0; i < MenuItemCount; i++)
  365. MenuItems[i]->draw(i - topline);
  366. if (onCursorDraw != nullptr) onCursorDraw(line());
  367. DWIN_UpdateLCD();
  368. }
  369. void MenuClass::onScroll(bool dir) {
  370. int8_t sel = selected;
  371. if (dir) sel++; else sel--;
  372. LIMIT(sel, 0, MenuItemCount - 1);
  373. if (sel != selected) {
  374. if (onCursorErase != nullptr) onCursorErase(line());
  375. DWIN_UpdateLCD();
  376. if ((sel - topline) == TROWS) {
  377. DWIN_Frame_AreaMove(1, DWIN_SCROLL_UP, MLINE, DWINUI::backcolor, 0, TITLE_HEIGHT + 1, DWIN_WIDTH, STATUS_Y - 1);
  378. topline++;
  379. MenuItems[sel]->draw(TROWS - 1);
  380. }
  381. if ((sel < topline)) {
  382. DWIN_Frame_AreaMove(1, DWIN_SCROLL_DOWN, MLINE, DWINUI::backcolor, 0, TITLE_HEIGHT + 1, DWIN_WIDTH, STATUS_Y - 1);
  383. topline--;
  384. MenuItems[sel]->draw(0);
  385. }
  386. selected = sel;
  387. if (onCursorDraw != nullptr) onCursorDraw(line());
  388. DWIN_UpdateLCD();
  389. }
  390. }
  391. void MenuClass::onClick() {
  392. if (MenuItems[selected]->onClick != nullptr) (*MenuItems[selected]->onClick)();
  393. }
  394. MenuItemClass *MenuClass::SelectedItem() {
  395. return MenuItems[selected];
  396. }
  397. MenuItemClass** MenuClass::Items() {
  398. return MenuItems;
  399. }
  400. int8_t MenuClass::count() {
  401. return MenuItemCount;
  402. };
  403. /* MenuItem Class ===========================================================*/
  404. MenuItemClass::MenuItemClass(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)()) {
  405. icon = cicon;
  406. onClick = onclick;
  407. onDraw = ondraw;
  408. const uint8_t len = _MIN(sizeof(caption) - 1, strlen(text));
  409. memcpy(&caption[0], text, len);
  410. caption[len] = '\0';
  411. }
  412. MenuItemClass::MenuItemClass(uint8_t cicon, uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)()) {
  413. icon = cicon;
  414. onClick = onclick;
  415. onDraw = ondraw;
  416. caption[0] = '\0';
  417. frameid = id;
  418. frame = { x1, y1, x2, y2 };
  419. }
  420. void MenuItemClass::SetFrame(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
  421. caption[0] = '\0';
  422. frameid = id;
  423. frame = { x1, y1, x2, y2 };
  424. }
  425. void MenuItemClass::draw(int8_t line) {
  426. if (!WITHIN(line, 0, TROWS - 1)) return;
  427. if (onDraw != nullptr) (*onDraw)(this, line);
  428. };
  429. void MenuItemClass::redraw() {
  430. draw(CurrentMenu->line(this->pos));
  431. }
  432. MenuItemPtrClass::MenuItemPtrClass(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)(), void* val) : MenuItemClass(cicon, text, ondraw, onclick) {
  433. value = val;
  434. };
  435. // Menu auxiliary functions ===================================================
  436. void MenuItemsClear() {
  437. if (MenuItems == nullptr) return;
  438. for (int8_t i = 0; i < MenuItemCount; i++) delete MenuItems[i];
  439. delete[] MenuItems;
  440. MenuItems = nullptr;
  441. MenuItemCount = 0;
  442. MenuItemTotal = 0;
  443. }
  444. void MenuItemsPrepare(int8_t totalitems) {
  445. MenuItemsClear();
  446. MenuItemTotal = totalitems;
  447. MenuItems = new MenuItemClass*[totalitems];
  448. }
  449. MenuItemClass* MenuItemsAdd(MenuItemClass* menuitem) {
  450. MenuItems[MenuItemCount] = menuitem;
  451. menuitem->pos = MenuItemCount++;
  452. return menuitem;
  453. }
  454. MenuItemClass* MenuItemsAdd(uint8_t cicon, const char * const text/*=nullptr*/, void (*ondraw)(MenuItemClass* menuitem, int8_t line)/*=nullptr*/, void (*onclick)()/*=nullptr*/) {
  455. if (MenuItemCount < MenuItemTotal) {
  456. MenuItemClass* menuitem = new MenuItemClass(cicon, text, ondraw, onclick);
  457. return MenuItemsAdd(menuitem);
  458. }
  459. else return nullptr;
  460. }
  461. MenuItemClass* MenuItemsAdd(uint8_t cicon, uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, void (*ondraw)(MenuItemClass* menuitem, int8_t line)/*=nullptr*/, void (*onclick)()/*=nullptr*/) {
  462. if (MenuItemCount < MenuItemTotal) {
  463. MenuItemClass* menuitem = new MenuItemClass(cicon, id, x1, y1, x2, y2, ondraw, onclick);
  464. return MenuItemsAdd(menuitem);
  465. }
  466. else return nullptr;
  467. }
  468. MenuItemClass* MenuItemsAdd(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)(), void* val) {
  469. if (MenuItemCount < MenuItemTotal) {
  470. MenuItemClass* menuitem = new MenuItemPtrClass(cicon, text, ondraw, onclick, val);
  471. return MenuItemsAdd(menuitem);
  472. }
  473. else return nullptr;
  474. }
  475. bool SetMenu(MenuClass* &menu, FSTR_P title, int8_t totalitems) {
  476. if (!menu) menu = new MenuClass();
  477. const bool NotCurrent = (CurrentMenu != menu);
  478. if (NotCurrent) {
  479. menu->MenuTitle.SetCaption(title);
  480. MenuItemsPrepare(totalitems);
  481. }
  482. return NotCurrent;
  483. }
  484. void UpdateMenu(MenuClass* &menu) {
  485. if (!menu) return;
  486. if (CurrentMenu != menu) {
  487. PreviousMenu = CurrentMenu;
  488. CurrentMenu = menu;
  489. }
  490. menu->draw();
  491. }
  492. void ReDrawMenu(const bool force/*=false*/) {
  493. if (CurrentMenu && (force || checkkey == Menu)) CurrentMenu->draw();
  494. if (force) DrawItemEdit();
  495. }
  496. #endif // DWIN_LCD_PROUI