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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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.4.1
  26. * Date: 2022/04/14
  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. //-----------------------------------------------------------------------------
  144. // On click functions
  145. //-----------------------------------------------------------------------------
  146. // Generic onclick event without draw
  147. // process: process id HMI destiny
  148. // lo: low limit
  149. // hi: high limit
  150. // dp: decimal places, 0 for integers
  151. // val: value / scaled value
  152. // LiveUpdate: live update function when the encoder changes
  153. // Apply: update function when the encoder is pressed
  154. 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*/) {
  155. checkkey = process;
  156. MenuData.MinValue = lo;
  157. MenuData.MaxValue = hi;
  158. MenuData.dp = dp;
  159. MenuData.Apply = Apply;
  160. MenuData.LiveUpdate = LiveUpdate;
  161. MenuData.Value = constrain(val, lo, hi);
  162. EncoderRate.enabled = true;
  163. }
  164. // Generic onclick event for integer values
  165. // process: process id HMI destiny
  166. // lo: scaled low limit
  167. // hi: scaled high limit
  168. // val: value
  169. // LiveUpdate: live update function when the encoder changes
  170. // Apply: update function when the encoder is pressed
  171. void SetValueOnClick(uint8_t process, const int32_t lo, const int32_t hi, const int32_t val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  172. SetOnClick(process, lo, hi, 0, val, Apply, LiveUpdate);
  173. Draw_Menu_IntValue(HMI_data.Selected_Color, CurrentMenu->line(), 4, MenuData.Value);
  174. }
  175. // Generic onclick event for float values
  176. // process: process id HMI destiny
  177. // lo: scaled low limit
  178. // hi: scaled high limit
  179. // val: value
  180. // LiveUpdate: live update function when the encoder changes
  181. // Apply: update function when the encoder is pressed
  182. void SetValueOnClick(uint8_t process, const float lo, const float hi, uint8_t dp, const float val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  183. const int32_t value = round(val * POW(10, dp));
  184. SetOnClick(process, lo * POW(10, dp), hi * POW(10, dp), dp, value, Apply, LiveUpdate);
  185. 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);
  186. }
  187. // Generic onclick event for integer values
  188. // lo: scaled low limit
  189. // hi: scaled high limit
  190. // val: value
  191. // LiveUpdate: live update function when the encoder changes
  192. // Apply: update function when the encoder is pressed
  193. void SetIntOnClick(const int32_t lo, const int32_t hi, const int32_t val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  194. SetValueOnClick(SetInt, lo, hi, val, Apply, LiveUpdate);
  195. }
  196. // Generic onclick event for set pointer to 16 bit uinteger values
  197. // lo: low limit
  198. // hi: high limit
  199. // LiveUpdate: live update function when the encoder changes
  200. // Apply: update function when the encoder is pressed
  201. void SetPIntOnClick(const int32_t lo, const int32_t hi, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  202. MenuData.P_Int = (int16_t*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
  203. const int32_t value = *MenuData.P_Int;
  204. SetValueOnClick(SetPInt, lo, hi, value, Apply, LiveUpdate);
  205. }
  206. // Generic onclick event for float values
  207. // process: process id HMI destiny
  208. // lo: low limit
  209. // hi: high limit
  210. // dp: decimal places
  211. // val: value
  212. void SetFloatOnClick(const float lo, const float hi, uint8_t dp, const float val, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  213. SetValueOnClick(SetFloat, lo, hi, dp, val, Apply, LiveUpdate);
  214. }
  215. // Generic onclick event for set pointer to float values
  216. // lo: low limit
  217. // hi: high limit
  218. // LiveUpdate: live update function when the encoder changes
  219. // Apply: update function when the encoder is pressed
  220. void SetPFloatOnClick(const float lo, const float hi, uint8_t dp, void (*Apply)() /*= nullptr*/, void (*LiveUpdate)() /*= nullptr*/) {
  221. MenuData.P_Float = (float*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
  222. SetValueOnClick(SetPFloat, lo, hi, dp, *MenuData.P_Float, Apply, LiveUpdate);
  223. }
  224. // HMI Control functions ======================================================
  225. // Generic menu control using the encoder
  226. void HMI_Menu() {
  227. EncoderState encoder_diffState = get_encoder_state();
  228. if (encoder_diffState == ENCODER_DIFF_NO) return;
  229. if (CurrentMenu) {
  230. if (encoder_diffState == ENCODER_DIFF_ENTER)
  231. CurrentMenu->onClick();
  232. else
  233. CurrentMenu->onScroll(encoder_diffState == ENCODER_DIFF_CW);
  234. }
  235. }
  236. // Get an integer value using the encoder without draw anything
  237. // lo: low limit
  238. // hi: high limit
  239. // Return value:
  240. // 0 : no change
  241. // 1 : live change
  242. // 2 : apply change
  243. int8_t HMI_GetIntNoDraw(const int32_t lo, const int32_t hi) {
  244. const int32_t cval = MenuData.Value;
  245. EncoderState encoder_diffState = Encoder_ReceiveAnalyze();
  246. if (encoder_diffState != ENCODER_DIFF_NO) {
  247. if (Apply_Encoder(encoder_diffState, MenuData.Value)) {
  248. EncoderRate.enabled = false;
  249. checkkey = Menu;
  250. return 2;
  251. }
  252. LIMIT(MenuData.Value, lo, hi);
  253. }
  254. return int8_t(cval != MenuData.Value);
  255. }
  256. // Get an integer value using the encoder
  257. // lo: low limit
  258. // hi: high limit
  259. // Return value:
  260. // 0 : no change
  261. // 1 : live change
  262. // 2 : apply change
  263. int8_t HMI_GetInt(const int32_t lo, const int32_t hi) {
  264. EncoderState encoder_diffState = Encoder_ReceiveAnalyze();
  265. if (encoder_diffState != ENCODER_DIFF_NO) {
  266. if (Apply_Encoder(encoder_diffState, MenuData.Value)) {
  267. EncoderRate.enabled = false;
  268. DWINUI::Draw_Signed_Int(HMI_data.Text_Color, HMI_data.Background_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, MenuData.Value);
  269. checkkey = Menu;
  270. return 2;
  271. }
  272. LIMIT(MenuData.Value, lo, hi);
  273. DWINUI::Draw_Signed_Int(HMI_data.Text_Color, HMI_data.Selected_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, MenuData.Value);
  274. return 1;
  275. }
  276. return 0;
  277. }
  278. // Set an integer using the encoder
  279. void HMI_SetInt() {
  280. int8_t val = HMI_GetInt(MenuData.MinValue, MenuData.MaxValue);
  281. switch (val) {
  282. case 0: return; break;
  283. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  284. case 2: if (MenuData.Apply) MenuData.Apply(); break;
  285. }
  286. }
  287. // Set an integer without drawing
  288. void HMI_SetIntNoDraw() {
  289. int8_t val = HMI_GetIntNoDraw(MenuData.MinValue, MenuData.MaxValue);
  290. switch (val) {
  291. case 0: return; break;
  292. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  293. case 2: if (MenuData.Apply) MenuData.Apply(); break;
  294. }
  295. }
  296. // Set an integer pointer variable using the encoder
  297. void HMI_SetPInt() {
  298. int8_t val = HMI_GetInt(MenuData.MinValue, MenuData.MaxValue);
  299. switch (val) {
  300. case 0: return;
  301. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  302. case 2: *MenuData.P_Int = MenuData.Value; if (MenuData.Apply) MenuData.Apply(); break;
  303. }
  304. }
  305. // Get a scaled float value using the encoder
  306. // dp: decimal places
  307. // lo: scaled low limit
  308. // hi: scaled high limit
  309. // Return value:
  310. // 0 : no change
  311. // 1 : live change
  312. // 2 : apply change
  313. int8_t HMI_GetFloat(uint8_t dp, int32_t lo, int32_t hi) {
  314. EncoderState encoder_diffState = Encoder_ReceiveAnalyze();
  315. if (encoder_diffState != ENCODER_DIFF_NO) {
  316. if (Apply_Encoder(encoder_diffState, MenuData.Value)) {
  317. EncoderRate.enabled = false;
  318. 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));
  319. checkkey = Menu;
  320. return 2;
  321. }
  322. LIMIT(MenuData.Value, lo, hi);
  323. DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Selected_Color, 3, dp, VALX - dp * DWINUI::fontWidth(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), MenuData.Value / POW(10, dp));
  324. return 1;
  325. }
  326. return 0;
  327. }
  328. // Set a scaled float using the encoder
  329. void HMI_SetFloat() {
  330. const int8_t val = HMI_GetFloat(MenuData.dp, MenuData.MinValue, MenuData.MaxValue);
  331. switch (val) {
  332. case 0: return;
  333. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  334. case 2: if (MenuData.Apply) MenuData.Apply(); break;
  335. }
  336. }
  337. // Set a scaled float pointer variable using the encoder
  338. void HMI_SetPFloat() {
  339. const int8_t val = HMI_GetFloat(MenuData.dp, MenuData.MinValue, MenuData.MaxValue);
  340. switch (val) {
  341. case 0: return;
  342. case 1: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
  343. case 2: *MenuData.P_Float = MenuData.Value / POW(10, MenuData.dp); if (MenuData.Apply) MenuData.Apply(); break;
  344. }
  345. }
  346. // Menu Classes ===============================================================
  347. MenuClass::MenuClass() {
  348. selected = 0;
  349. topline = 0;
  350. }
  351. void MenuClass::draw() {
  352. MenuTitle.draw();
  353. if (onMenuDraw != nullptr) onMenuDraw(this);
  354. for (int8_t i = 0; i < MenuItemCount; i++)
  355. MenuItems[i]->draw(i - topline);
  356. if (onCursorDraw != nullptr) onCursorDraw(line());
  357. DWIN_UpdateLCD();
  358. }
  359. void MenuClass::onScroll(bool dir) {
  360. int8_t sel = selected;
  361. if (dir) sel++; else sel--;
  362. LIMIT(sel, 0, MenuItemCount - 1);
  363. if (sel != selected) {
  364. if (onCursorErase != nullptr) onCursorErase(line());
  365. DWIN_UpdateLCD();
  366. if ((sel - topline) == TROWS) {
  367. DWIN_Frame_AreaMove(1, DWIN_SCROLL_UP, MLINE, DWINUI::backcolor, 0, TITLE_HEIGHT + 1, DWIN_WIDTH, STATUS_Y - 1);
  368. topline++;
  369. MenuItems[sel]->draw(TROWS - 1);
  370. }
  371. if ((sel < topline)) {
  372. DWIN_Frame_AreaMove(1, DWIN_SCROLL_DOWN, MLINE, DWINUI::backcolor, 0, TITLE_HEIGHT + 1, DWIN_WIDTH, STATUS_Y - 1);
  373. topline--;
  374. MenuItems[sel]->draw(0);
  375. }
  376. selected = sel;
  377. if (onCursorDraw != nullptr) onCursorDraw(line());
  378. DWIN_UpdateLCD();
  379. }
  380. }
  381. void MenuClass::onClick() {
  382. if (MenuItems[selected]->onClick != nullptr) (*MenuItems[selected]->onClick)();
  383. }
  384. MenuItemClass *MenuClass::SelectedItem() {
  385. return MenuItems[selected];
  386. }
  387. MenuItemClass** MenuClass::Items() {
  388. return MenuItems;
  389. }
  390. int8_t MenuClass::count() {
  391. return MenuItemCount;
  392. };
  393. /* MenuItem Class ===========================================================*/
  394. MenuItemClass::MenuItemClass(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)()) {
  395. icon = cicon;
  396. onClick = onclick;
  397. onDraw = ondraw;
  398. const uint8_t len = _MIN(sizeof(caption) - 1, strlen(text));
  399. memcpy(&caption[0], text, len);
  400. caption[len] = '\0';
  401. }
  402. 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)()) {
  403. icon = cicon;
  404. onClick = onclick;
  405. onDraw = ondraw;
  406. caption[0] = '\0';
  407. frameid = id;
  408. frame = { x1, y1, x2, y2 };
  409. }
  410. void MenuItemClass::SetFrame(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
  411. caption[0] = '\0';
  412. frameid = id;
  413. frame = { x1, y1, x2, y2 };
  414. }
  415. void MenuItemClass::draw(int8_t line) {
  416. if (line < 0 || line >= TROWS) return;
  417. if (onDraw != nullptr) (*onDraw)(this, line);
  418. };
  419. void MenuItemClass::redraw() {
  420. draw(CurrentMenu->line(this->pos));
  421. }
  422. 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) {
  423. value = val;
  424. };
  425. // Menu auxiliary functions ===================================================
  426. void MenuItemsClear() {
  427. if (MenuItems == nullptr) return;
  428. for (int8_t i = 0; i < MenuItemCount; i++) delete MenuItems[i];
  429. delete[] MenuItems;
  430. MenuItems = nullptr;
  431. MenuItemCount = 0;
  432. MenuItemTotal = 0;
  433. }
  434. void MenuItemsPrepare(int8_t totalitems) {
  435. MenuItemsClear();
  436. MenuItemTotal = totalitems;
  437. MenuItems = new MenuItemClass*[totalitems];
  438. }
  439. MenuItemClass* MenuItemsAdd(MenuItemClass* menuitem) {
  440. MenuItems[MenuItemCount] = menuitem;
  441. menuitem->pos = MenuItemCount++;
  442. return menuitem;
  443. }
  444. MenuItemClass* MenuItemsAdd(uint8_t cicon, const char * const text/*=nullptr*/, void (*ondraw)(MenuItemClass* menuitem, int8_t line)/*=nullptr*/, void (*onclick)()/*=nullptr*/) {
  445. if (MenuItemCount < MenuItemTotal) {
  446. MenuItemClass* menuitem = new MenuItemClass(cicon, text, ondraw, onclick);
  447. return MenuItemsAdd(menuitem);
  448. }
  449. else return nullptr;
  450. }
  451. 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*/) {
  452. if (MenuItemCount < MenuItemTotal) {
  453. MenuItemClass* menuitem = new MenuItemClass(cicon, id, x1, y1, x2, y2, ondraw, onclick);
  454. return MenuItemsAdd(menuitem);
  455. }
  456. else return nullptr;
  457. }
  458. MenuItemClass* MenuItemsAdd(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)(), void* val) {
  459. if (MenuItemCount < MenuItemTotal) {
  460. MenuItemClass* menuitem = new MenuItemPtrClass(cicon, text, ondraw, onclick, val);
  461. return MenuItemsAdd(menuitem);
  462. }
  463. else return nullptr;
  464. }
  465. bool SetMenu(MenuClass* &menu, FSTR_P title, int8_t totalitems) {
  466. if (!menu) menu = new MenuClass();
  467. const bool NotCurrent = (CurrentMenu != menu);
  468. if (NotCurrent) {
  469. menu->MenuTitle.SetCaption(title);
  470. MenuItemsPrepare(totalitems);
  471. }
  472. return NotCurrent;
  473. }
  474. void UpdateMenu(MenuClass* &menu) {
  475. if (!menu) return;
  476. if (CurrentMenu != menu) {
  477. PreviousMenu = CurrentMenu;
  478. CurrentMenu = menu;
  479. }
  480. menu->draw();
  481. }
  482. void ReDrawMenu() { if (CurrentMenu && checkkey==Menu) CurrentMenu->draw(); }
  483. #endif // DWIN_LCD_PROUI