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.

command_processor.h 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. /***********************
  2. * command_processor.h *
  3. ***********************/
  4. /****************************************************************************
  5. * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
  6. * *
  7. * This program is free software: you can redistribute it and/or modify *
  8. * it under the terms of the GNU General Public License as published by *
  9. * the Free Software Foundation, either version 3 of the License, or *
  10. * (at your option) any later version. *
  11. * *
  12. * This program is distributed in the hope that it will be useful, *
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  15. * GNU General Public License for more details. *
  16. * *
  17. * To view a copy of the GNU General Public License, go to the following *
  18. * location: <https://www.gnu.org/licenses/>. *
  19. ****************************************************************************/
  20. #pragma once
  21. typedef struct {
  22. uint32_t bg;
  23. uint32_t grad;
  24. uint32_t fg;
  25. uint32_t rgb;
  26. } btn_colors;
  27. // Disable TOUCH_UI_FIT_TEXT on a case-by-case basis
  28. namespace FTDI {
  29. constexpr uint16_t OPT_NOFIT = OPT_NOTICKS;
  30. }
  31. /**************************** Enhanced Command Processor **************************/
  32. /* The CommandProcessor class wraps the CommandFifo with several features to make
  33. * defining user interfaces much easier.
  34. *
  35. * - Implements chaining on all methods
  36. * - Automatically adds text to button, toggle, text and keys.
  37. * - Constrains all widgets to fit inside a box for ease of layout.
  38. * - Font size is specified using a chained modifier.
  39. * - Option argument is given the default OPT_3D value.
  40. */
  41. class CommandProcessor : public CLCD::CommandFifo {
  42. public:
  43. static constexpr uint8_t STYLE_DISABLED = 0x80;
  44. private:
  45. static bool default_button_style_func(CommandProcessor &, uint8_t tag, uint8_t & /*style*/, uint16_t &options, bool) {
  46. if (tag != 0 && FTDI::EventLoop::get_pressed_tag() == tag) {
  47. options = FTDI::OPT_FLAT;
  48. }
  49. return false;
  50. }
  51. typedef bool btn_style_func_t(CommandProcessor &cmd, uint8_t tag, uint8_t &style, uint16_t &options, bool post);
  52. static btn_style_func_t *_btn_style_callback;
  53. static bool is_tracking;
  54. int8_t _font = 26, _tag = 0;
  55. uint8_t _style = 0;
  56. protected:
  57. // Returns the canonical thickness of a widget (i.e. the height of a toggle element)
  58. uint16_t widget_thickness() {
  59. CLCD::FontMetrics fm(_font);
  60. return fm.height * 20.0/16;
  61. }
  62. FORCEDINLINE void linear_widget_box(int16_t &x, int16_t &y, int16_t &w, int16_t &h, bool tracker = false) {
  63. const uint16_t th = widget_thickness() / 2;
  64. if (w > h) {
  65. x += tracker ? th * 2.5 : th;
  66. y += (h - th) / 2;
  67. w -= tracker ? th * 5.0 : th * 2;
  68. h = th;
  69. }
  70. else {
  71. x += (w - th) / 2;
  72. y += tracker ? th * 2.5 : th;
  73. w = th;
  74. h -= tracker ? th * 5.0 : th * 2;
  75. }
  76. }
  77. FORCEDINLINE uint16_t circular_widget_box(int16_t &x, int16_t &y, int16_t &w, int16_t &h) {
  78. const uint16_t r = min(w,h) / 2;
  79. x += w / 2;
  80. y += h / 2;
  81. w = 1;
  82. h = 1;
  83. return r;
  84. }
  85. public:
  86. // Helper method for setting all colors at once
  87. inline CommandProcessor& colors(const btn_colors &colors) {
  88. cmd(FTDI::COLOR_RGB(colors.rgb))
  89. .gradcolor(colors.grad)
  90. .fgcolor(colors.fg)
  91. .bgcolor(colors.bg);
  92. return *this;
  93. }
  94. inline CommandProcessor& bitmap_size(uint8_t filter, uint8_t wrapx, uint8_t wrapy, uint16_t width, uint16_t height) {
  95. cmd(FTDI::BITMAP_SIZE(filter, wrapx, wrapy, width, height));
  96. #if FTDI_API_LEVEL >= 810
  97. if (FTDI::ftdi_chip >= 810)
  98. cmd(FTDI::BITMAP_SIZE_H(width >> 9, height >> 9));
  99. #endif
  100. return *this;
  101. }
  102. inline CommandProcessor& bitmap_layout(uint8_t format, uint16_t linestride, uint16_t height) {
  103. cmd(FTDI::BITMAP_LAYOUT(format, linestride, height));
  104. #if FTDI_API_LEVEL >= 810
  105. if (FTDI::ftdi_chip >= 810)
  106. cmd(FTDI::BITMAP_LAYOUT_H(linestride >> 10, height >> 9));
  107. #endif
  108. return *this;
  109. }
  110. inline CommandProcessor& set_button_style_callback(const btn_style_func_t *func) {
  111. _btn_style_callback = func ? func : default_button_style_func;
  112. return *this;
  113. }
  114. inline CommandProcessor& tag (uint8_t tag) {_tag = tag; cmd(FTDI::TAG(tag)); return *this;}
  115. inline CommandProcessor& font (int16_t font) {_font = font; return *this;}
  116. inline CommandProcessor& enabled (bool enabled=false) {
  117. if (enabled)
  118. _style &= ~STYLE_DISABLED;
  119. else
  120. _style |= STYLE_DISABLED;
  121. return *this;
  122. }
  123. inline CommandProcessor& style (uint8_t style) {
  124. _style = (_style & STYLE_DISABLED) | style;
  125. return *this;
  126. }
  127. bool wait();
  128. uint32_t memcrc(uint32_t ptr, uint32_t num);
  129. // Wrap all the CommandFifo routines to allow method chaining
  130. inline CommandProcessor& cmd (uint32_t cmd32) {CLCD::CommandFifo::cmd(cmd32); return *this;}
  131. inline CommandProcessor& cmd (void *data, uint16_t len) {CLCD::CommandFifo::cmd(data, len); return *this;}
  132. inline CommandProcessor& execute() {CLCD::CommandFifo::execute(); return *this;}
  133. inline CommandProcessor& fgcolor (uint32_t rgb) {CLCD::CommandFifo::fgcolor(rgb); return *this;}
  134. inline CommandProcessor& bgcolor (uint32_t rgb) {CLCD::CommandFifo::bgcolor(rgb); return *this;}
  135. inline CommandProcessor& gradcolor(uint32_t rgb) {CLCD::CommandFifo::gradcolor(rgb); return *this;}
  136. inline CommandProcessor& snapshot (uint32_t ptr) {CLCD::CommandFifo::snapshot(ptr); return *this;}
  137. inline CommandProcessor& loadimage(uint32_t ptr, uint32_t options)
  138. {CLCD::CommandFifo::loadimage(ptr, options); return *this;}
  139. inline CommandProcessor& sketch (int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t ptr, uint16_t format)
  140. {CLCD::CommandFifo::sketch(x, y, w, h, ptr, format); return *this;}
  141. inline CommandProcessor& screensaver () {CLCD::CommandFifo::screensaver(); return *this;}
  142. #if FTDI_API_LEVEL >= 810
  143. inline CommandProcessor& setbase (uint8_t base) {CLCD::CommandFifo::setbase(base); return *this;}
  144. #endif
  145. inline CommandProcessor& loadidentity () {CLCD::CommandFifo::loadidentity(); return *this;}
  146. inline CommandProcessor& scale (int32_t sx, int32_t sy) {CLCD::CommandFifo::scale(sx,sy); return *this;}
  147. inline CommandProcessor& rotate (int32_t a) {CLCD::CommandFifo::rotate(a); return *this;}
  148. inline CommandProcessor& translate(int32_t tx, int32_t ty) {CLCD::CommandFifo::translate(tx,ty); return *this;}
  149. inline CommandProcessor& setmatrix () {CLCD::CommandFifo::setmatrix(); return *this;}
  150. inline CommandProcessor& stop () {CLCD::CommandFifo::stop(); return *this;}
  151. inline CommandProcessor& memzero (uint32_t ptr, uint32_t size)
  152. {CLCD::CommandFifo::memzero(ptr, size); return *this;}
  153. inline CommandProcessor& memset (uint32_t ptr, uint32_t val, uint32_t size)
  154. {CLCD::CommandFifo::memset(ptr, val, size); return *this;}
  155. inline CommandProcessor& memcpy (uint32_t src, uint32_t dst, uint32_t size)
  156. {CLCD::CommandFifo::memcpy(src, dst, size); return *this;}
  157. inline CommandProcessor& memcrc (uint32_t ptr, uint32_t num, uint32_t result)
  158. {CLCD::CommandFifo::memcrc(ptr, num, result); return *this;}
  159. inline CommandProcessor& memwrite (uint32_t ptr, uint32_t value)
  160. {CLCD::CommandFifo::memwrite(ptr, value); return *this;}
  161. inline CommandProcessor& inflate (uint32_t ptr)
  162. {CLCD::CommandFifo::inflate(ptr); return *this;}
  163. inline CommandProcessor& getptr (uint32_t result)
  164. {CLCD::CommandFifo::getptr(result); return *this;}
  165. inline CommandProcessor& getprops (uint32_t ptr, uint32_t width, uint32_t height)
  166. {CLCD::CommandFifo::getprops(ptr, width, height); return *this;}
  167. #if FTDI_API_LEVEL >= 810
  168. inline CommandProcessor& setbitmap (uint32_t ptr, uint16_t fmt, uint16_t w, uint16_t h)
  169. {CLCD::CommandFifo::setbitmap(ptr,fmt,w,h); return *this;}
  170. inline CommandProcessor& snapshot2 (uint32_t fmt, uint32_t ptr, int16_t x, int16_t y, uint16_t w, uint16_t h)
  171. {CLCD::CommandFifo::snapshot2(fmt,ptr,x,y,w,h); return *this;}
  172. inline CommandProcessor& mediafifo (uint32_t p, uint32_t s) {CLCD::CommandFifo::mediafifo(p, s); return *this;}
  173. inline CommandProcessor& playvideo(uint32_t options) {CLCD::CommandFifo::playvideo(options); return *this;}
  174. inline CommandProcessor& romfont(uint8_t font, uint8_t slot) {CLCD::CommandFifo::romfont(font, slot); return *this;}
  175. #endif
  176. inline CommandProcessor& gradient(int16_t x0, int16_t y0, uint32_t rgb0, int16_t x1, int16_t y1, uint32_t rgb1)
  177. {CLCD::CommandFifo::gradient(x0,y0,rgb0,x1,y1,rgb1); return *this;}
  178. inline CommandProcessor& rectangle(int16_t x, int16_t y, int16_t w, int16_t h) {
  179. using namespace FTDI;
  180. CLCD::CommandFifo::cmd(BEGIN(RECTS));
  181. CLCD::CommandFifo::cmd(VERTEX2F( x * 16, y * 16));
  182. CLCD::CommandFifo::cmd(VERTEX2F((x + w) * 16, (y + h) * 16));
  183. return *this;
  184. }
  185. inline CommandProcessor& border(int16_t x, int16_t y, int16_t w, int16_t h) {
  186. using namespace FTDI;
  187. CLCD::CommandFifo::cmd(BEGIN(LINES));
  188. CLCD::CommandFifo::cmd(VERTEX2F( x * 16, y * 16));
  189. CLCD::CommandFifo::cmd(VERTEX2F((x + w) * 16, y * 16));
  190. CLCD::CommandFifo::cmd(VERTEX2F((x + w) * 16, y * 16));
  191. CLCD::CommandFifo::cmd(VERTEX2F((x + w) * 16, (y + h) * 16));
  192. CLCD::CommandFifo::cmd(VERTEX2F((x + w) * 16, (y + h) * 16));
  193. CLCD::CommandFifo::cmd(VERTEX2F( x * 16, (y + h) * 16));
  194. CLCD::CommandFifo::cmd(VERTEX2F( x * 16, (y + h) * 16));
  195. CLCD::CommandFifo::cmd(VERTEX2F( x * 16, y * 16));
  196. return *this;
  197. }
  198. template<typename T>
  199. FORCEDINLINE CommandProcessor& toggle(int16_t x, int16_t y, int16_t w, int16_t h, T text, bool state, uint16_t options = FTDI::OPT_3D) {
  200. CLCD::FontMetrics fm(_font);
  201. const int16_t widget_h = fm.height * 20.0 / 16;
  202. //const int16_t outer_bar_r = widget_h / 2;
  203. //const int16_t knob_r = outer_bar_r - 1.5;
  204. // The y coordinate of the toggle is the baseline of the text,
  205. // so we must introduce a fudge factor based on the line height to
  206. // actually center the control.
  207. const int16_t fudge_y = fm.height * 5 / 16;
  208. CLCD::CommandFifo::toggle(x + h / 2, y + (h - widget_h) / 2 + fudge_y, w - h, _font, options, state);
  209. CLCD::CommandFifo::str(text);
  210. return *this;
  211. }
  212. CommandProcessor& toggle2(int16_t x, int16_t y, int16_t w, int16_t h, progmem_str no, progmem_str yes, bool state, uint16_t options = FTDI::OPT_3D) {
  213. char text[strlen_P((const char *)no) + strlen_P((const char *)yes) + 2];
  214. strcpy_P(text, (const char *)no);
  215. strcat(text, "\xFF");
  216. strcat_P(text, (const char *)yes);
  217. return toggle(x, y, w, h, text, state, options);
  218. }
  219. // Constrained drawing routines. These constrain the widget inside a box for easier layout.
  220. // The FORCEDINLINE ensures that the code is inlined so that all the math is done at compile time.
  221. FORCEDINLINE CommandProcessor& track_linear(int16_t x, int16_t y, int16_t w, int16_t h, int16_t tag) {
  222. linear_widget_box(x, y, w, h, true);
  223. CLCD::CommandFifo::track(x, y, w, h, tag);
  224. is_tracking = true;
  225. return *this;
  226. }
  227. FORCEDINLINE CommandProcessor& track_circular(int16_t x, int16_t y, int16_t w, int16_t h, int16_t tag) {
  228. circular_widget_box(x,y, w, h);
  229. CLCD::CommandFifo::track(x, y, w, h, tag);
  230. is_tracking = true;
  231. return *this;
  232. }
  233. uint8_t track_tag (uint16_t &value) {
  234. if (is_tracking) {
  235. if (FTDI::EventLoop::is_touch_held()) {
  236. return CLCD::get_tracker(value);
  237. }
  238. else {
  239. CLCD::CommandFifo::track(0, 0, 0, 0, 0);
  240. CLCD::CommandFifo::execute();
  241. is_tracking = false;
  242. }
  243. }
  244. return 0;
  245. }
  246. FORCEDINLINE CommandProcessor& clock(int16_t x, int16_t y, int16_t w, int16_t h, int16_t hr, int16_t m, int16_t s, int16_t ms, uint16_t options = FTDI::OPT_3D) {
  247. const uint16_t r = circular_widget_box(x, y, w, h);
  248. CLCD::CommandFifo::clock(x, y, r, options, hr, m, s, ms);
  249. return *this;
  250. }
  251. FORCEDINLINE CommandProcessor& gauge(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t major, uint16_t minor, uint16_t val, uint16_t range, uint16_t options = FTDI::OPT_3D) {
  252. const uint16_t r = circular_widget_box(x, y, w, h);
  253. CLCD::CommandFifo::gauge(x, y, r, options, major, minor, val, range);
  254. return *this;
  255. }
  256. FORCEDINLINE CommandProcessor& dial(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t val, uint16_t options = FTDI::OPT_3D) {
  257. const uint16_t r = circular_widget_box(x, y, w, h);
  258. CLCD::CommandFifo::dial(x, y, r, options, val);
  259. return *this;
  260. }
  261. FORCEDINLINE CommandProcessor& slider(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t val, uint16_t range, uint16_t options = FTDI::OPT_3D) {
  262. linear_widget_box(x, y, w, h);
  263. CLCD::CommandFifo::slider(x, y, w, h, options, val, range);
  264. return *this;
  265. }
  266. FORCEDINLINE CommandProcessor& progress(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t val, uint16_t range, uint16_t options = FTDI::OPT_3D) {
  267. linear_widget_box(x, y, w, h);
  268. CLCD::CommandFifo::progress(x, y, w, h, options, val, range);
  269. return *this;
  270. }
  271. FORCEDINLINE CommandProcessor& scrollbar(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t val, uint16_t size, uint16_t range, uint16_t options = 0) {
  272. linear_widget_box(x, y, w, h);
  273. CLCD::CommandFifo::scrollbar(x, y, w, h, options, val, size, range);
  274. return *this;
  275. }
  276. void apply_text_alignment(int16_t &x, int16_t &y, int16_t w, int16_t h, uint16_t options) {
  277. using namespace FTDI;
  278. x += ((options & OPT_CENTERX) ? w/2 : ((options & OPT_RIGHTX) ? w : 0));
  279. y += ((options & OPT_CENTERY) ? h/2 : h);
  280. }
  281. // Reduce font size until text fits the enclosing box.
  282. template<typename T>
  283. int8_t apply_fit_text(int16_t w, int16_t h, T text) {
  284. using namespace FTDI;
  285. int8_t font = _font;
  286. #if ENABLED(TOUCH_UI_USE_UTF8)
  287. const bool is_utf8 = has_utf8_chars(text);
  288. #endif
  289. for (;font > 26;) {
  290. int16_t width, height;
  291. #if ENABLED(TOUCH_UI_USE_UTF8)
  292. if (is_utf8) {
  293. width = get_utf8_text_width(text, font_size_t::from_romfont(font));
  294. height = font_size_t::from_romfont(font).get_height();
  295. }
  296. else
  297. #endif
  298. {
  299. CLCD::FontMetrics fm(font);
  300. width = fm.get_text_width(text);
  301. height = fm.height;
  302. }
  303. if (width < w && height < h) break;
  304. font--;
  305. }
  306. return font;
  307. }
  308. CommandProcessor& number(int16_t x, int16_t y, int16_t w, int16_t h, int32_t n, uint16_t options = FTDI::OPT_CENTER) {
  309. using namespace FTDI;
  310. apply_text_alignment(x, y, w, h, options);
  311. CLCD::CommandFifo::number(x, y, _font, options, n);
  312. return *this;
  313. }
  314. template<typename T>
  315. uint16_t text_width(T text) {
  316. using namespace FTDI;
  317. #if ENABLED(TOUCH_UI_USE_UTF8)
  318. if (has_utf8_chars(text))
  319. return get_utf8_text_width(text, font_size_t::from_romfont(_font));
  320. #endif
  321. CLCD::FontMetrics fm(_font);
  322. return fm.get_text_width(text);
  323. }
  324. template<typename T>
  325. CommandProcessor& text(int16_t x, int16_t y, int16_t w, int16_t h, T text, uint16_t options = FTDI::OPT_CENTER) {
  326. using namespace FTDI;
  327. apply_text_alignment(x, y, w, h, options);
  328. #ifdef TOUCH_UI_FIT_TEXT
  329. const int8_t font = (options & OPT_NOFIT) ? _font : apply_fit_text(w, h, text);
  330. #else
  331. const int8_t font = _font;
  332. #endif
  333. #if ENABLED(TOUCH_UI_USE_UTF8)
  334. if (has_utf8_chars(text))
  335. draw_utf8_text(*this, x, y, text, font_size_t::from_romfont(font), options);
  336. else
  337. #endif
  338. {
  339. CLCD::CommandFifo::text(x, y, font, options);
  340. CLCD::CommandFifo::str(text);
  341. }
  342. return *this;
  343. }
  344. FORCEDINLINE CommandProcessor& icon(int16_t x, int16_t y, int16_t w, int16_t h, const FTDI::bitmap_info_t& info, const float scale = 1) {
  345. using namespace FTDI;
  346. cmd(BEGIN(BITMAPS));
  347. if (scale != 1) {
  348. cmd(BITMAP_TRANSFORM_A(uint32_t(float(256)/scale)));
  349. cmd(BITMAP_TRANSFORM_E(uint32_t(float(256)/scale)));
  350. }
  351. cmd(BITMAP_SIZE(info.filter, info.wrapx, info.wrapy, info.width*scale, info.height*scale));
  352. cmd(VERTEX2F((x + w/2 - info.width*scale/2)*16, (y + h/2 - info.height*scale/2)*16));
  353. if (scale != 1) {
  354. cmd(BITMAP_TRANSFORM_A(256));
  355. cmd(BITMAP_TRANSFORM_E(256));
  356. }
  357. return *this;
  358. }
  359. template<typename T>
  360. CommandProcessor& button(int16_t x, int16_t y, int16_t w, int16_t h, T text, uint16_t options = FTDI::OPT_3D) {
  361. using namespace FTDI;
  362. bool styleModified = false;
  363. if (_btn_style_callback) styleModified = _btn_style_callback(*this, _tag, _style, options, false);
  364. #ifdef TOUCH_UI_FIT_TEXT
  365. const int8_t font = (options & OPT_NOFIT) ? _font : apply_fit_text(w, h, text);
  366. #else
  367. const int8_t font = _font;
  368. #endif
  369. CLCD::CommandFifo::button(x, y, w, h, font, options);
  370. #if ENABLED(TOUCH_UI_USE_UTF8)
  371. if (has_utf8_chars(text)) {
  372. CLCD::CommandFifo::str(F(""));
  373. apply_text_alignment(x, y, w, h, OPT_CENTER);
  374. if (!(options & FTDI::OPT_FLAT)) {
  375. // Reproduce the black "shadow" the FTDI adds to the button label
  376. CLCD::CommandFifo::cmd(SAVE_CONTEXT());
  377. CLCD::CommandFifo::cmd(COLOR_RGB(0x00000));
  378. draw_utf8_text(*this, x-1, y-1, text, font_size_t::from_romfont(font), OPT_CENTER);
  379. CLCD::CommandFifo::cmd(RESTORE_CONTEXT());
  380. }
  381. // Draw the button label
  382. draw_utf8_text(*this, x, y, text, font_size_t::from_romfont(font), OPT_CENTER);
  383. }
  384. else
  385. #endif
  386. CLCD::CommandFifo::str(text);
  387. if (_btn_style_callback && styleModified) _btn_style_callback(*this, _tag, _style, options, true);
  388. return *this;
  389. }
  390. template<typename T>
  391. CommandProcessor& keys(int16_t x, int16_t y, int16_t w, int16_t h, T keys, uint16_t options = FTDI::OPT_3D) {
  392. CLCD::CommandFifo::keys(x, y, w, h, _font, options);
  393. CLCD::CommandFifo::str(keys);
  394. return *this;
  395. }
  396. FORCEDINLINE CommandProcessor& spinner(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t style = 0, uint16_t scale = 0) {
  397. circular_widget_box(x, y, w, h);
  398. CLCD::CommandFifo::spinner(x, y, style, scale);
  399. return *this;
  400. }
  401. };