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.

ui_common.cpp 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2021 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. #include "../../../inc/MarlinConfigPre.h"
  23. #if IS_DWIN_MARLINUI
  24. #include "marlinui_dwin.h"
  25. #include "dwin_lcd.h"
  26. #include "dwin_string.h"
  27. //#include "../../lcdprint.h"
  28. #include "lcdprint_dwin.h"
  29. #include "../../fontutils.h"
  30. #include "../../../libs/numtostr.h"
  31. #include "../../marlinui.h"
  32. #include "../../../sd/cardreader.h"
  33. #include "../../../module/motion.h"
  34. #include "../../../module/temperature.h"
  35. #include "../../../module/printcounter.h"
  36. #if ENABLED(SDSUPPORT)
  37. #include "../../../libs/duration_t.h"
  38. #endif
  39. #if ENABLED(AUTO_BED_LEVELING_UBL)
  40. #include "../../../feature/bedlevel/bedlevel.h"
  41. #endif
  42. // DWIN printing specifies the font on each string operation
  43. // but we'll make the font modal for Marlin
  44. dwin_font_t dwin_font = { font8x16, 8, 16, Color_White, Color_Bg_Black, true };
  45. void MarlinUI::set_font(const uint8_t font_nr) {
  46. if (font_nr != dwin_font.index) {
  47. dwin_font.index = font_nr;
  48. uint8_t w, h;
  49. switch (font_nr) {
  50. default:
  51. case font6x12: w = 6; h = 12; break;
  52. case font8x16: w = 8; h = 16; break;
  53. case font10x20: w = 10; h = 20; break;
  54. case font12x24: w = 12; h = 24; break;
  55. case font14x28: w = 14; h = 28; break;
  56. case font16x32: w = 16; h = 32; break;
  57. case font20x40: w = 20; h = 40; break;
  58. case font24x48: w = 24; h = 48; break;
  59. case font28x56: w = 28; h = 56; break;
  60. case font32x64: w = 32; h = 64; break;
  61. }
  62. dwin_font.width = w;
  63. dwin_font.height = h;
  64. // TODO: Array with dimensions, auto fit menu items,
  65. // update char width / height of the screen based on
  66. // new (fixed-width) font size.
  67. }
  68. }
  69. // This display is always detected
  70. bool MarlinUI::detected() { return true; }
  71. // Initialize or re-initialize the LCD
  72. void MarlinUI::init_lcd() { DWIN_Startup(); }
  73. // This LCD should clear where it will draw anew
  74. void MarlinUI::clear_lcd() {
  75. DWIN_ICON_AnimationControl(0x0000); // disable all icon animations
  76. DWIN_JPG_ShowAndCache(3);
  77. DWIN_Frame_Clear(Color_Bg_Black);
  78. DWIN_UpdateLCD();
  79. did_first_redraw = false;
  80. }
  81. #if ENABLED(SHOW_BOOTSCREEN)
  82. void MarlinUI::show_bootscreen() {
  83. dwin_string.set(F(SHORT_BUILD_VERSION));
  84. #if ENABLED(SHOW_CUSTOM_BOOTSCREEN) && !defined(CUSTOM_BOOTSCREEN_TIMEOUT)
  85. #define CUSTOM_BOOTSCREEN_TIMEOUT 3000
  86. #endif
  87. #if ENABLED(DWIN_MARLINUI_PORTRAIT)
  88. #define LOGO_CENTER ((LCD_PIXEL_WIDTH) / 2)
  89. #define INFO_CENTER LOGO_CENTER
  90. #define VERSION_Y 330
  91. #else
  92. #define LOGO_CENTER (280 / 2)
  93. #define INFO_CENTER ((LCD_PIXEL_WIDTH) - 200 / 2)
  94. #define VERSION_Y 84
  95. #endif
  96. DWIN_Draw_String(false, font10x20, Color_Yellow, Color_Bg_Black, INFO_CENTER - (dwin_string.length * 10) / 2, VERSION_Y, S(dwin_string.string()));
  97. TERN_(SHOW_CUSTOM_BOOTSCREEN, safe_delay(CUSTOM_BOOTSCREEN_TIMEOUT));
  98. clear_lcd();
  99. DWIN_ICON_Show(BOOT_ICON, ICON_MarlinBoot, LOGO_CENTER - 266 / 2, 15);
  100. #if ENABLED(DWIN_MARLINUI_PORTRAIT)
  101. DWIN_ICON_Show(BOOT_ICON, ICON_OpenSource, LOGO_CENTER - 174 / 2, 280);
  102. DWIN_ICON_Show(BOOT_ICON, ICON_GitHubURL, LOGO_CENTER - 180 / 2, 420);
  103. DWIN_ICON_Show(BOOT_ICON, ICON_MarlinURL, LOGO_CENTER - 100 / 2, 440);
  104. DWIN_ICON_Show(BOOT_ICON, ICON_Copyright, LOGO_CENTER - 126 / 2, 460);
  105. #else
  106. DWIN_ICON_Show(BOOT_ICON, ICON_MarlinBoot, LOGO_CENTER - 266 / 2, 15);
  107. DWIN_ICON_Show(BOOT_ICON, ICON_OpenSource, INFO_CENTER - 174 / 2, 60);
  108. DWIN_ICON_Show(BOOT_ICON, ICON_GitHubURL, INFO_CENTER - 180 / 2, 130);
  109. DWIN_ICON_Show(BOOT_ICON, ICON_MarlinURL, INFO_CENTER - 100 / 2, 152);
  110. DWIN_ICON_Show(BOOT_ICON, ICON_Copyright, INFO_CENTER - 126 / 2, 200);
  111. #endif
  112. DWIN_Draw_String(false, font10x20, Color_Yellow, Color_Bg_Black, INFO_CENTER - (dwin_string.length * 10) / 2, VERSION_Y, S(dwin_string.string()));
  113. DWIN_UpdateLCD();
  114. }
  115. void MarlinUI::bootscreen_completion(const millis_t sofar) {
  116. if ((BOOTSCREEN_TIMEOUT) > sofar) safe_delay((BOOTSCREEN_TIMEOUT) - sofar);
  117. clear_lcd();
  118. }
  119. #endif
  120. // The kill screen is displayed for unrecoverable conditions
  121. void MarlinUI::draw_kill_screen() {
  122. set_font(DWIN_FONT_ALERT);
  123. DWIN_Frame_Clear(Color_Bg_Black);
  124. dwin_font.fg = Color_Error_Red;
  125. dwin_font.solid = false;
  126. DWIN_Draw_Rectangle(1, Color_Bg_Window, 20, 20, LCD_PIXEL_WIDTH - 20, LCD_PIXEL_HEIGHT - 20);
  127. // make the frame a few pixels thick
  128. DWIN_Draw_Rectangle(0, Color_Yellow, 20, 20, LCD_PIXEL_WIDTH - 20, LCD_PIXEL_HEIGHT - 20);
  129. DWIN_Draw_Rectangle(0, Color_Yellow, 21, 21, LCD_PIXEL_WIDTH - 21, LCD_PIXEL_HEIGHT - 21);
  130. DWIN_Draw_Rectangle(0, Color_Yellow, 22, 22, LCD_PIXEL_WIDTH - 22, LCD_PIXEL_HEIGHT - 22);
  131. uint8_t cx = (LCD_PIXEL_WIDTH / dwin_font.width / 2),
  132. cy = (LCD_PIXEL_HEIGHT / dwin_font.height / 2);
  133. #if ENABLED(DWIN_MARLINUI_LANDSCAPE)
  134. cx += (96 / 2 / dwin_font.width);
  135. DWIN_ICON_Show(ICON, ICON_Halted, 40, (LCD_PIXEL_HEIGHT - 96) / 2);
  136. #else
  137. DWIN_ICON_Show(ICON, ICON_Halted, (LCD_PIXEL_WIDTH - 96) / 2, 40);
  138. #endif
  139. uint8_t slen = utf8_strlen(status_message);
  140. lcd_moveto(cx - (slen / 2), cy - 1);
  141. lcd_put_u8str(status_message);
  142. slen = utf8_strlen(S(GET_TEXT_F(MSG_HALTED)));
  143. lcd_moveto(cx - (slen / 2), cy);
  144. lcd_put_u8str(GET_TEXT_F(MSG_HALTED));
  145. slen = utf8_strlen(S(GET_TEXT_F(MSG_HALTED)));
  146. lcd_moveto(cx - (slen / 2), cy + 1);
  147. lcd_put_u8str(GET_TEXT_F(MSG_HALTED));
  148. }
  149. //
  150. // Status Message
  151. //
  152. void MarlinUI::draw_status_message(const bool blink) {
  153. set_font(DWIN_FONT_STAT);
  154. dwin_font.solid = true;
  155. dwin_font.fg = Color_White;
  156. dwin_font.bg = Color_Bg_Black;
  157. lcd_moveto_xy(0, LCD_PIXEL_HEIGHT - (STAT_FONT_HEIGHT) - 1);
  158. constexpr uint8_t max_status_chars = (LCD_PIXEL_WIDTH) / (STAT_FONT_WIDTH);
  159. auto status_changed = []{
  160. static uint16_t old_hash = 0x0000;
  161. uint16_t hash = 0x0000;
  162. for (uint8_t i = 0; i < MAX_MESSAGE_LENGTH; i++) {
  163. const char c = ui.status_message[i];
  164. if (!c) break;
  165. hash = ((hash << 1) | (hash >> 15)) ^ c;
  166. }
  167. const bool hash_changed = hash != old_hash;
  168. old_hash = hash;
  169. return hash_changed || !ui.did_first_redraw;
  170. };
  171. #if ENABLED(STATUS_MESSAGE_SCROLLING)
  172. static bool last_blink = false;
  173. // Get the UTF8 character count of the string
  174. uint8_t slen = utf8_strlen(status_message);
  175. // If the string fits into the LCD, just print it and do not scroll it
  176. if (slen <= max_status_chars) {
  177. if (status_changed()) {
  178. // The string isn't scrolling and may not fill the screen
  179. lcd_put_u8str(status_message);
  180. // Fill the rest with spaces
  181. while (slen < max_status_chars) { lcd_put_wchar(' '); ++slen; }
  182. }
  183. }
  184. else {
  185. // String is larger than the available line space
  186. // Get a pointer to the next valid UTF8 character
  187. // and the string remaining length
  188. uint8_t rlen;
  189. const char *stat = status_and_len(rlen);
  190. lcd_put_u8str_max(stat, max_status_chars);
  191. // If the string doesn't completely fill the line...
  192. if (rlen < max_status_chars) {
  193. lcd_put_wchar('.'); // Always at 1+ spaces left, draw a dot
  194. uint8_t chars = max_status_chars - rlen; // Amount of space left in characters
  195. if (--chars) { // Draw a second dot if there's space
  196. lcd_put_wchar('.');
  197. if (--chars)
  198. lcd_put_u8str_max(status_message, chars); // Print a second copy of the message
  199. }
  200. }
  201. if (last_blink != blink) {
  202. last_blink = blink;
  203. advance_status_scroll();
  204. }
  205. }
  206. #else
  207. UNUSED(blink);
  208. if (status_changed()) {
  209. // Get the UTF8 character count of the string
  210. uint8_t slen = utf8_strlen(status_message);
  211. // Just print the string to the LCD
  212. lcd_put_u8str_max(status_message, max_status_chars);
  213. // Fill the rest with spaces if there are missing spaces
  214. while (slen < max_status_chars) { lcd_put_wchar(' '); ++slen; }
  215. }
  216. #endif
  217. }
  218. #if HAS_LCD_BRIGHTNESS
  219. void MarlinUI::_set_brightness() { DWIN_LCD_Brightness(backlight ? brightness : 0); }
  220. #endif
  221. #if HAS_MARLINUI_MENU
  222. #include "../../menu/menu.h"
  223. #if ENABLED(ADVANCED_PAUSE_FEATURE)
  224. void MarlinUI::draw_hotend_status(const uint8_t row, const uint8_t extruder) {
  225. dwin_font.solid = false;
  226. dwin_font.fg = Color_White;
  227. dwin_string.set("E");
  228. dwin_string.add('1' + extruder);
  229. dwin_string.add(' ');
  230. dwin_string.add(i16tostr3rj(thermalManager.degHotend(extruder)));
  231. dwin_string.add('/');
  232. if (get_blink() || !thermalManager.heater_idle[thermalManager.idle_index_for_id(extruder)].timed_out)
  233. dwin_string.add(i16tostr3rj(thermalManager.degTargetHotend(extruder)));
  234. else
  235. dwin_string.add(PSTR(" "));
  236. lcd_moveto(LCD_WIDTH - dwin_string.length, row);
  237. lcd_put_dwin_string();
  238. }
  239. #endif
  240. // Set the colors for a menu item based on whether it is selected
  241. static bool mark_as_selected(const uint8_t row, const bool sel, const bool is_static=false) {
  242. const dwin_coord_t y = row * (MENU_LINE_HEIGHT) + 1;
  243. if (y >= LCD_PIXEL_HEIGHT) return false;
  244. if (is_static && sel)
  245. DWIN_Draw_Box(1, Color_Bg_Heading, 0, y, LCD_PIXEL_WIDTH, MENU_LINE_HEIGHT - 1);
  246. else {
  247. #if ENABLED(MENU_HOLLOW_FRAME)
  248. DWIN_Draw_Box(1, Color_Bg_Black, 0, y, LCD_PIXEL_WIDTH, MENU_LINE_HEIGHT - 1);
  249. if (sel) DWIN_Draw_Box(0, Select_Color, 0, y, LCD_PIXEL_WIDTH, MENU_LINE_HEIGHT - 1);
  250. #else
  251. DWIN_Draw_Box(1, sel ? Select_Color : Color_Bg_Black, 0, y, LCD_PIXEL_WIDTH, MENU_LINE_HEIGHT - 1);
  252. #endif
  253. }
  254. return true;
  255. }
  256. // Draw a static line of text in the same idiom as a menu item
  257. void MenuItem_static::draw(const uint8_t row, FSTR_P const ftpl, const uint8_t style/*=SS_DEFAULT*/, const char * const vstr/*=nullptr*/) {
  258. // Call mark_as_selected to draw a bigger selection box
  259. // and draw the text without a background
  260. if (mark_as_selected(row, (bool)(style & SS_INVERT), true)) {
  261. ui.set_font(DWIN_FONT_MENU);
  262. dwin_font.solid = false;
  263. dwin_font.fg = Color_White;
  264. dwin_string.set();
  265. const int8_t plen = ftpl ? utf8_strlen(ftpl) : 0,
  266. vlen = vstr ? utf8_strlen(vstr) : 0;
  267. if (style & SS_CENTER) {
  268. int8_t pad = (LCD_WIDTH - 1 - plen - vlen) / 2;
  269. while (--pad) dwin_string.add(' ');
  270. }
  271. if (plen) dwin_string.add(ftpl, itemIndex, itemStringC, itemStringF);
  272. if (vlen) dwin_string.add(vstr);
  273. if (style & SS_CENTER) {
  274. int8_t pad = (LCD_WIDTH - 1 - plen - vlen) / 2;
  275. while (--pad) dwin_string.add(' ');
  276. }
  277. lcd_moveto(1, row);
  278. lcd_put_dwin_string();
  279. }
  280. }
  281. // Draw a generic menu item
  282. void MenuItemBase::_draw(const bool sel, const uint8_t row, FSTR_P const ftpl, const char, const char post_char) {
  283. if (mark_as_selected(row, sel)) {
  284. ui.set_font(DWIN_FONT_MENU);
  285. dwin_font.solid = false;
  286. dwin_font.fg = Color_White;
  287. dwin_string.set(ftpl, itemIndex, itemStringC, itemStringF);
  288. pixel_len_t n = LCD_WIDTH - 1 - dwin_string.length;
  289. while (--n > 1) dwin_string.add(' ');
  290. dwin_string.add(post_char);
  291. lcd_moveto(1, row);
  292. lcd_put_dwin_string();
  293. }
  294. }
  295. //
  296. // Draw a menu item with an editable value
  297. //
  298. void MenuEditItemBase::draw(const bool sel, const uint8_t row, FSTR_P const ftpl, const char * const inStr, const bool pgm) {
  299. if (mark_as_selected(row, sel)) {
  300. ui.set_font(DWIN_FONT_MENU);
  301. dwin_font.solid = false;
  302. dwin_font.fg = Color_White;
  303. const uint8_t vallen = (pgm ? utf8_strlen_P(inStr) : utf8_strlen(S(inStr)));
  304. dwin_string.set(ftpl, itemIndex, itemStringC, itemStringF);
  305. if (vallen) dwin_string.add(':');
  306. lcd_moveto(1, row);
  307. lcd_put_dwin_string();
  308. if (vallen) {
  309. dwin_font.fg = Color_Yellow;
  310. dwin_string.set(inStr);
  311. lcd_moveto(LCD_WIDTH - vallen - 1, row);
  312. lcd_put_dwin_string();
  313. }
  314. }
  315. }
  316. //
  317. // Draw an edit screen with label and current value
  318. //
  319. void MenuEditItemBase::draw_edit_screen(FSTR_P const fstr, const char* const value/*=nullptr*/) {
  320. ui.encoder_direction_normal();
  321. const dwin_coord_t labellen = utf8_strlen(fstr), vallen = utf8_strlen(value);
  322. dwin_string.set(FTOP(fstr), itemIndex);
  323. if (vallen) dwin_string.add(':'); // If a value is included, add a colon
  324. // Assume the label is alpha-numeric (with a descender)
  325. const uint16_t row = (LCD_HEIGHT / 2) - 1;
  326. dwin_font.fg = Color_White;
  327. dwin_font.solid = true;
  328. lcd_moveto((LCD_WIDTH - labellen + !!vallen) / 2, row);
  329. lcd_put_dwin_string();
  330. // If a value is included, print the value in larger text below the label
  331. if (vallen) {
  332. dwin_string.set(value);
  333. const dwin_coord_t by = (row * MENU_LINE_HEIGHT) + MENU_FONT_HEIGHT + EXTRA_ROW_HEIGHT / 2;
  334. DWIN_Draw_String(true, font16x32, Color_Yellow, Color_Bg_Black, (LCD_PIXEL_WIDTH - vallen * 16) / 2, by, S(dwin_string.string()));
  335. extern screenFunc_t _manual_move_func_ptr;
  336. if (ui.currentScreen != _manual_move_func_ptr && !ui.external_control) {
  337. const dwin_coord_t slider_length = LCD_PIXEL_WIDTH - TERN(DWIN_MARLINUI_LANDSCAPE, 120, 20),
  338. slider_height = 16,
  339. slider_x = (LCD_PIXEL_WIDTH - slider_length) / 2,
  340. slider_y = by + 32 + 4,
  341. amount = ui.encoderPosition * slider_length / maxEditValue;
  342. DWIN_Draw_Rectangle(1, Color_Bg_Window, slider_x - 1, slider_y - 1, slider_x - 1 + slider_length + 2 - 1, slider_y - 1 + slider_height + 2 - 1);
  343. if (amount > 0)
  344. DWIN_Draw_Box(1, BarFill_Color, slider_x, slider_y, amount, slider_height);
  345. if (amount < slider_length)
  346. DWIN_Draw_Box(1, Color_Bg_Black, slider_x + amount, slider_y, slider_length - amount, slider_height);
  347. }
  348. }
  349. }
  350. inline void draw_boxed_string(const bool yesopt, FSTR_P const fstr, const bool inv) {
  351. const uint8_t len = utf8_strlen(fstr),
  352. mar = TERN(DWIN_MARLINUI_PORTRAIT, 1, 4),
  353. col = yesopt ? LCD_WIDTH - mar - len : mar,
  354. row = (LCD_HEIGHT >= 8 ? LCD_HEIGHT / 2 + 3 : LCD_HEIGHT - 1);
  355. lcd_moveto(col, row);
  356. DWIN_Draw_Box(1, inv ? Select_Color : Color_Bg_Black, cursor.x - dwin_font.width, cursor.y + 1, dwin_font.width * (len + 2), dwin_font.height + 2);
  357. lcd_put_u8str(col, row, fstr);
  358. }
  359. void MenuItem_confirm::draw_select_screen(
  360. FSTR_P const yes, FSTR_P const no, const bool yesno,
  361. FSTR_P const pref, const char * const string/*=nullptr*/, FSTR_P const suff/*=nullptr*/
  362. ) {
  363. ui.set_font(DWIN_FONT_MENU);
  364. dwin_font.solid = false;
  365. dwin_font.fg = Color_White;
  366. ui.draw_select_screen_prompt(pref, string, suff);
  367. if (no) draw_boxed_string(false, no, !yesno);
  368. if (yes) draw_boxed_string(true, yes, yesno);
  369. }
  370. #if ENABLED(SDSUPPORT)
  371. void MenuItem_sdbase::draw(const bool sel, const uint8_t row, FSTR_P const, CardReader &theCard, const bool isDir) {
  372. if (mark_as_selected(row, sel)) {
  373. dwin_string.set();
  374. uint8_t maxlen = LCD_WIDTH - 1;
  375. if (isDir) {
  376. dwin_string.add(LCD_STR_FOLDER " ");
  377. maxlen -= 2;
  378. }
  379. dwin_string.add(ui.scrolled_filename(theCard, maxlen, row, sel), maxlen);
  380. uint8_t n = maxlen - dwin_string.length;
  381. while (n > 0) { dwin_string.add(' '); --n; }
  382. lcd_moveto(1, row);
  383. lcd_put_dwin_string();
  384. }
  385. }
  386. #endif // SDSUPPORT
  387. #if ENABLED(AUTO_BED_LEVELING_UBL)
  388. /**
  389. * UBL LCD "radar" map data
  390. */
  391. #define MAP_UPPER_LEFT_CORNER_X 5 // These probably should be moved to the .h file But for now,
  392. #define MAP_UPPER_LEFT_CORNER_Y 5 // it is easier to play with things having them here
  393. #define MAP_MAX_PIXELS_X 262 // 272 - 10
  394. #define MAP_MAX_PIXELS_Y 262
  395. void MarlinUI::ubl_plot(const uint8_t x_plot, const uint8_t y_plot) {
  396. // Scale the box pixels appropriately
  397. dwin_coord_t x_map_pixels = ((MAP_MAX_PIXELS_X - 4) / (GRID_MAX_POINTS_X)) * (GRID_MAX_POINTS_X),
  398. y_map_pixels = ((MAP_MAX_PIXELS_Y - 4) / (GRID_MAX_POINTS_Y)) * (GRID_MAX_POINTS_Y),
  399. pixels_per_x_mesh_pnt = x_map_pixels / (GRID_MAX_POINTS_X),
  400. pixels_per_y_mesh_pnt = y_map_pixels / (GRID_MAX_POINTS_Y),
  401. x_offset = MAP_UPPER_LEFT_CORNER_X + 1 + (MAP_MAX_PIXELS_X - x_map_pixels - 2) / 2,
  402. y_offset = MAP_UPPER_LEFT_CORNER_Y + 1 + (MAP_MAX_PIXELS_Y - y_map_pixels - 2) / 2;
  403. // Clear the Mesh Map
  404. // First draw the bigger box in White so we have a border around the mesh map box
  405. DWIN_Draw_Rectangle(1, Color_White, x_offset - 2, y_offset - 2, x_offset + 2 + x_map_pixels, y_offset + 2 + y_map_pixels);
  406. // Now actually clear the mesh map box
  407. DWIN_Draw_Rectangle(1, Color_Bg_Black, x_offset, y_offset, x_offset + x_map_pixels, y_offset + y_map_pixels);
  408. // Fill in the Specified Mesh Point
  409. const uint8_t y_plot_inv = (GRID_MAX_POINTS_Y - 1) - y_plot; // The origin is typically in the lower right corner. We need to
  410. // invert the Y to get it to plot in the right location.
  411. const dwin_coord_t by = y_offset + y_plot_inv * pixels_per_y_mesh_pnt;
  412. DWIN_Draw_Rectangle(1, Select_Color,
  413. x_offset + (x_plot * pixels_per_x_mesh_pnt), by,
  414. x_offset + (x_plot * pixels_per_x_mesh_pnt) + pixels_per_x_mesh_pnt, by + pixels_per_y_mesh_pnt
  415. );
  416. // Display Mesh Point Locations
  417. const dwin_coord_t sx = x_offset + pixels_per_x_mesh_pnt / 2;
  418. dwin_coord_t y = y_offset + pixels_per_y_mesh_pnt / 2;
  419. for (uint8_t j = 0; j < (GRID_MAX_POINTS_Y); j++, y += pixels_per_y_mesh_pnt)
  420. for (uint8_t i = 0, x = sx; i < (GRID_MAX_POINTS_X); i++, x += pixels_per_x_mesh_pnt)
  421. DWIN_Draw_Point(Color_White, 1, 1, x, y);
  422. // Put Relevant Text on Display
  423. // Show X and Y positions at top of screen
  424. dwin_font.fg = Color_White;
  425. dwin_font.solid = true;
  426. const xy_pos_t pos = { bedlevel.get_mesh_x(x_plot), bedlevel.get_mesh_y(y_plot) },
  427. lpos = pos.asLogical();
  428. lcd_moveto(
  429. TERN(DWIN_MARLINUI_LANDSCAPE, ((x_offset + x_map_pixels) / MENU_FONT_WIDTH) + 2, 1),
  430. TERN(DWIN_MARLINUI_LANDSCAPE, 1, ((y_offset + y_map_pixels) / MENU_LINE_HEIGHT) + 1)
  431. );
  432. lcd_put_u8str_P(X_LBL);
  433. lcd_put_u8str(ftostr52(lpos.x));
  434. lcd_moveto(
  435. TERN(DWIN_MARLINUI_LANDSCAPE, ((x_offset + x_map_pixels) / MENU_FONT_WIDTH) + 2, 1),
  436. TERN(DWIN_MARLINUI_LANDSCAPE, 3, ((y_offset + y_map_pixels) / MENU_LINE_HEIGHT) + 2)
  437. );
  438. lcd_put_u8str_P(Y_LBL);
  439. lcd_put_u8str(ftostr52(lpos.y));
  440. // Print plot position
  441. dwin_string.set("(");
  442. dwin_string.add(i8tostr3rj(x_plot));
  443. dwin_string.add(",");
  444. dwin_string.add(i8tostr3rj(y_plot));
  445. dwin_string.add(")");
  446. lcd_moveto(
  447. TERN(DWIN_MARLINUI_LANDSCAPE, ((x_offset + x_map_pixels) / MENU_FONT_WIDTH) + 2, LCD_WIDTH - dwin_string.length),
  448. TERN(DWIN_MARLINUI_LANDSCAPE, LCD_HEIGHT - 2, ((y_offset + y_map_pixels) / MENU_LINE_HEIGHT) + 1)
  449. );
  450. lcd_put_dwin_string();
  451. // Show the location value
  452. dwin_string.set(Z_LBL);
  453. if (!isnan(bedlevel.z_values[x_plot][y_plot]))
  454. dwin_string.add(ftostr43sign(bedlevel.z_values[x_plot][y_plot]));
  455. else
  456. dwin_string.add(PSTR(" -----"));
  457. lcd_moveto(
  458. TERN(DWIN_MARLINUI_LANDSCAPE, ((x_offset + x_map_pixels) / MENU_FONT_WIDTH) + 2, LCD_WIDTH - dwin_string.length),
  459. TERN(DWIN_MARLINUI_LANDSCAPE, LCD_HEIGHT - 1, ((y_offset + y_map_pixels) / MENU_LINE_HEIGHT) + 2)
  460. );
  461. lcd_put_dwin_string();
  462. }
  463. #endif // AUTO_BED_LEVELING_UBL
  464. #if ANY(BABYSTEP_ZPROBE_GFX_OVERLAY, MESH_EDIT_GFX_OVERLAY)
  465. void MarlinUI::zoffset_overlay(const int8_t dir) {
  466. const int rot_up = TERN(OVERLAY_GFX_REVERSE, ICON_RotateCCW, ICON_RotateCW),
  467. rot_down = TERN(OVERLAY_GFX_REVERSE, ICON_RotateCW, ICON_RotateCCW);
  468. const int nozzle = (LCD_PIXEL_WIDTH / 2) - 20;
  469. // Draw a representation of the nozzle
  470. DWIN_Draw_Box(1, Color_Bg_Black, nozzle + 3, 8, 48, 52); // 'clear' the area where the nozzle is drawn in case it was moved up/down
  471. DWIN_ICON_Show(ICON, ICON_HotendOff, nozzle + 3, 10 - dir);
  472. DWIN_ICON_Show(ICON, ICON_BedLine, nozzle, 10 + 36);
  473. // Draw cw/ccw indicator and up/down arrows
  474. const int arrow_y = LCD_PIXEL_HEIGHT / 2 - 24;
  475. DWIN_ICON_Show(ICON, ICON_DownArrow, 0, arrow_y - dir);
  476. DWIN_ICON_Show(ICON, rot_down, 48, arrow_y);
  477. DWIN_ICON_Show(ICON, ICON_UpArrow, LCD_PIXEL_WIDTH - 10 - (48*2), arrow_y - dir);
  478. DWIN_ICON_Show(ICON, rot_up, LCD_PIXEL_WIDTH - 10 - 48, arrow_y);
  479. }
  480. #endif // BABYSTEP_ZPROBE_GFX_OVERLAY || MESH_EDIT_GFX_OVERLAY
  481. #endif // HAS_MARLINUI_MENU
  482. #endif // IS_DWIN_MARLINUI