panel.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. #include "door.h"
  2. #include <set>
  3. #include <string.h>
  4. /**
  5. * @file
  6. * @brief Panels and Menus
  7. */
  8. /**
  9. * @example menu-example.cpp
  10. *
  11. * @brief Example using menus
  12. *
  13. * This example shows a menu, and demonstrates the new menu update ability.
  14. *
  15. */
  16. // #include <memory>
  17. namespace door {
  18. Panel::Panel(int xp, int yp, int panelWidth) : border_color() {
  19. x = xp;
  20. y = yp;
  21. width = panelWidth;
  22. hidden = false;
  23. border_style = BorderStyle::NONE;
  24. // border_color = ANSIColor();
  25. }
  26. Panel::Panel(int panelWidth) : border_color() {
  27. x = 0;
  28. y = 0;
  29. width = panelWidth;
  30. hidden = false;
  31. border_style = BorderStyle::NONE;
  32. }
  33. Panel::Panel(Panel &&ref) {
  34. x = ref.x;
  35. y = ref.y;
  36. width = ref.width;
  37. hidden = ref.hidden;
  38. border_style = ref.border_style;
  39. title = std::move(ref.title);
  40. offset = ref.offset;
  41. lines = std::move(ref.lines);
  42. }
  43. /*
  44. Panel::Panel(const Panel &original) : border_color(original.border_color) {
  45. x = original.x;
  46. y = original.y;
  47. width = original.width;
  48. hidden = original.hidden;
  49. border_style = original.border_style;
  50. // door::Line title_copy(*original.title);
  51. // title = std::move(std::make_unique<door::Line>(title_copy));
  52. // What's wrong with making copies of unique_ptr objects?
  53. title = std::move(std::make_unique<door::Line>(*original.title));
  54. offset = original.offset;
  55. for (auto &line : original.lines) {
  56. // door::Line l(*line);
  57. // lines.push_back(std::move(std::make_unique<door::Line>(l)));
  58. // door::Line l(*line);
  59. lines.push_back(std::move(std::make_unique<door::Line>(*line)));
  60. }
  61. }
  62. */
  63. /**
  64. * @brief Set the panels X and Y screen position.
  65. *
  66. * @param[in] xp,yp new screen position
  67. */
  68. void Panel::set(int xp, int yp) {
  69. x = xp;
  70. y = yp;
  71. }
  72. void Panel::setTitle(std::unique_ptr<Line> t, int off) {
  73. title = std::move(t);
  74. offset = off;
  75. }
  76. void Panel::setStyle(BorderStyle bs) { border_style = bs; }
  77. // Panel::Panel(Panel &old) = { }
  78. void Panel::setColor(ANSIColor c) { border_color = c; }
  79. void Panel::hide(void) { hidden = true; }
  80. void Panel::show(void) { hidden = false; }
  81. void Panel::addLine(std::unique_ptr<Line> l) {
  82. l->fit();
  83. lines.push_back(std::move(l));
  84. }
  85. // or possibly std::move(l)); }
  86. /*
  87. bool Panel::delLine(std::shared_ptr<Line> l) {
  88. size_t size = lines.size();
  89. remove(lines.begin(), lines.end(), l);
  90. return (size != lines.size());
  91. }
  92. */
  93. /**
  94. * Utility structure that stores the characters needed to
  95. * render boxes.
  96. *
  97. * We assume that Top can be used in Bottom and Middle.
  98. * We assume that Side can be used on Left and Right.
  99. */
  100. struct box_styles {
  101. /// Top Left
  102. const char *tl;
  103. /// Top Right
  104. const char *tr;
  105. /// Top
  106. const char *top;
  107. /// Side
  108. const char *side;
  109. /// Bottom Left
  110. const char *bl;
  111. /// Bottom Right
  112. const char *br;
  113. /// Middle Left
  114. const char *ml;
  115. /// Middle Right
  116. const char *mr;
  117. };
  118. /**
  119. *
  120. * use https://en.wikipedia.org/wiki/Code_page_437 for translations between
  121. * CP437 and unicode symbols.
  122. *
  123. * This holds the characters needed to render the different box styles.
  124. * tl tr top side bl br ml mr
  125. */
  126. /**
  127. * @brief Unicode box characters
  128. *
  129. * top-left, top-right, top, side, bottom-left, bottom-right, middle-left,
  130. * middle-right See \ref BorderStyle for the order the boxes are in.
  131. */
  132. struct box_styles UBOXES[] = {{"\u250c", "\u2510", "\u2500", "\u2502", "\u2514",
  133. "\u2518", "\u251c", "\u2524"},
  134. {"\u2554", "\u2557", "\u2550", "\u2551", "\u255a",
  135. "\u255d", "\u2560", "\u2563"},
  136. {"\u2553", "\u2556", "\u2500", "\u2551", "\u2559",
  137. "\u255c", "\u255f", "\u2562"},
  138. {"\u2552", "\u2555", "\u2550", "\u2502", "\u2558",
  139. "\u255b", "\u255e", "\u2561"}};
  140. /**
  141. * @brief CP437 box characters
  142. *
  143. * top-left, top-right, top, side, bottom-left, bottom-right, middle-left,
  144. * middle-right See \ref BorderStyle for the order the boxes are in.
  145. */
  146. struct box_styles BOXES[] = {
  147. /*
  148. # ┌──┐
  149. # │ │
  150. # ├──┤
  151. # └──┘
  152. */
  153. {
  154. "\xda",
  155. "\xbf",
  156. "\xc4",
  157. "\xb3",
  158. "\xc0",
  159. "\xd9",
  160. "\xc3",
  161. "\xb4",
  162. },
  163. /*
  164. # ╔══╗
  165. # ║ ║
  166. # ╠══╣
  167. # ╚══╝
  168. */
  169. {
  170. "\xc9",
  171. "\xbb",
  172. "\xcd",
  173. "\xba",
  174. "\xc8",
  175. "\xbc",
  176. "\xcc",
  177. "\xb9",
  178. },
  179. /*
  180. # ╓──╖
  181. # ║ ║
  182. # ╟──╢
  183. # ╙──╜
  184. */
  185. {
  186. "\xd6",
  187. "\xb7",
  188. "\xc4",
  189. "\xba",
  190. "\xd3",
  191. "\xbd",
  192. "\xc7",
  193. "\xb6",
  194. },
  195. /*
  196. # ╒══╕
  197. # │ │
  198. # ╞══╡
  199. # ╘══╛
  200. */
  201. {
  202. "\xd5",
  203. "\xb8",
  204. "\xcd",
  205. "\xb3",
  206. "\xd4",
  207. "\xbe",
  208. "\xc6",
  209. "\xb5",
  210. },
  211. };
  212. // These already exists in the above structures. Whoops.
  213. /**
  214. * @brief CP437 box line joining data
  215. *
  216. * SINGLE 0, DOUBLE 1
  217. * Join Border to Line, 0 is Left, 1 is Right.
  218. */
  219. const char *JOIN[2][2][2] = {{
  220. {"\xc3", "\xb4"}, // SS 00
  221. {"\xc6", "\xb5"} // SD 01
  222. },
  223. {
  224. {"\xc7", "\xb6"}, // DS 10
  225. {"\xcc", "\xb9"}, // DD 11
  226. }};
  227. /**
  228. * @brief Unicode box line joining data
  229. * SINGLE 0, DOUBLE 1
  230. * Join Border to Line, 0 is Left, 1 is Right.
  231. */
  232. const char *UJOIN[2][2][2] = {{
  233. {"\u251c", "\u2524"}, // SS ├ ┤
  234. {"\u255e", "\u2561"} // SD ╞ ╡
  235. },
  236. {
  237. {"\u255f", "\u2562"}, // DS ╟ ╢
  238. {"\u2560", "\u2563"}, // DD ╠ ╣
  239. }};
  240. /*
  241. void Panel::display(void) {
  242. }
  243. void Panel::update(void) {
  244. }
  245. */
  246. bool Panel::update(Door &d) {
  247. int row = y;
  248. int style = (int)border_style;
  249. if (style > 0)
  250. ++row;
  251. bool updated = false;
  252. for (auto &line : lines) {
  253. if (line->update()) {
  254. /*
  255. std::string output = d.previous.debug();
  256. d.log(output);
  257. output = "update():";
  258. output.append(line->debug());
  259. d.log(output);
  260. */
  261. updated = true;
  262. int col = x;
  263. if (style > 0)
  264. ++col;
  265. d << door::Goto(col, row);
  266. d << *line;
  267. }
  268. ++row;
  269. }
  270. return updated;
  271. }
  272. void Panel::update(Door &d, int line) {
  273. int row = y;
  274. int style = (int)border_style;
  275. if (style > 0)
  276. ++row;
  277. // ok, I have the line number to update.
  278. auto &l = lines[line];
  279. row += line;
  280. int col = x;
  281. if (style > 0)
  282. ++col;
  283. d << door::Goto(col, row);
  284. d << *l;
  285. }
  286. void Panel::update(void) {
  287. for (auto &line : lines) {
  288. line->update();
  289. }
  290. }
  291. door::Goto Panel::gotoEnd(void) {
  292. int row = y;
  293. int style = (int)border_style;
  294. if (style > 0)
  295. ++row;
  296. row += lines.size();
  297. int col = x;
  298. if (style > 0)
  299. col += 2;
  300. col += width;
  301. return door::Goto(col, row);
  302. }
  303. /**
  304. * @brief Set background of all lines in the panel.
  305. *
  306. * @param back
  307. */
  308. void Panel::lineSetBack(ANSIColor back) {
  309. for (auto &line : lines) {
  310. line->setColor(back);
  311. }
  312. }
  313. /**
  314. * @brief Create a spacer line using block drawing characters.
  315. *
  316. * Return a Line of single or double characters the width of the panel.
  317. * @param[in] single make single or double line
  318. * @return std::unique_ptr<Line>
  319. */
  320. std::unique_ptr<Line> Panel::spacer_line(bool single) {
  321. std::string spacer_text;
  322. if (door::unicode) {
  323. for (int x = 0; x < width; ++x) {
  324. if (single)
  325. spacer_text.append(UBOXES[0].top);
  326. else
  327. spacer_text.append(UBOXES[1].top);
  328. }
  329. } else {
  330. if (single)
  331. spacer_text = std::string(width, BOXES[0].top[0]);
  332. else
  333. spacer_text = std::string(width, BOXES[1].top[0]);
  334. }
  335. std::unique_ptr<Line> line = make_unique<Line>(spacer_text, width);
  336. return line;
  337. }
  338. // operator<< Panel is called to output the Menu.
  339. // Menu has been massively changed to use Render instead of Colorizer.
  340. /**
  341. * @brief Output panel to stream
  342. *
  343. * This uses the Panel.x, Panel.y to render the panel using ANSI control codes.
  344. * Border style is considered, and wether or not we are using unicode.
  345. * Colors of the border, and lines use their color or their renderFunction.
  346. * @param os
  347. * @param p
  348. * @return std::ostream&
  349. */
  350. std::ostream &operator<<(std::ostream &os, const Panel &p) {
  351. if (p.hidden)
  352. return os;
  353. // Handle borders
  354. int style = (int)p.border_style;
  355. struct box_styles s;
  356. // If there's no style, then everything works.
  357. // If I try style, it prints out first line
  358. // and dies. (Yet it says there's 4 lines!)
  359. if (style > 0) {
  360. // Ok, there needs to be something in this style;
  361. if (style < 5) {
  362. if (unicode)
  363. s = UBOXES[style - 1];
  364. else
  365. s = BOXES[style - 1];
  366. } else {
  367. s.bl = s.br = s.mr = s.ml = " ";
  368. s.top = s.side = " ";
  369. s.tl = s.tr = " ";
  370. }
  371. }
  372. /*
  373. Door *d = dynamic_cast<Door *>(&os);
  374. if (d != nullptr) {
  375. */
  376. /*
  377. os << "style " << style << "."
  378. << " width " << p.width;
  379. os << " SIZE " << p.lines.size() << " ! " << nl;
  380. */
  381. // os << s.tl << s.top << s.tr << s.side << s.br << s.bl;
  382. int row = p.y;
  383. if (style > 0) {
  384. // Top line of border (if needed)
  385. os << door::Goto(p.x, row);
  386. os << p.border_color << s.tl;
  387. if (p.title) {
  388. for (int c = 0; c < p.offset; c++)
  389. os << s.top;
  390. os << *(p.title);
  391. os << p.border_color;
  392. int left = p.width - (p.offset + (p.title)->length());
  393. if (left > 0) {
  394. for (int c = 0; c < left; c++)
  395. os << s.top;
  396. };
  397. os << s.tr;
  398. } else {
  399. for (int c = 0; c < p.width; c++)
  400. os << s.top;
  401. os << s.tr;
  402. };
  403. // os << "";
  404. ++row;
  405. };
  406. for (auto &line : p.lines) {
  407. os << door::Goto(p.x, row);
  408. bool join = false;
  409. int line_is = -1;
  410. int border_is = -1;
  411. // is this a weird line?
  412. {
  413. const char *line_text = line->getText();
  414. if (door::unicode) {
  415. if (strncmp(UBOXES[0].top, line_text, strlen(UBOXES[0].top)) == 0) {
  416. join = true;
  417. line_is = 0;
  418. }
  419. if (strncmp(UBOXES[1].top, line_text, strlen(UBOXES[1].top)) == 0) {
  420. join = true;
  421. line_is = 1;
  422. }
  423. } else {
  424. if (BOXES[0].top[0] == line_text[0]) {
  425. join = true;
  426. line_is = 0;
  427. }
  428. if (BOXES[1].top[0] == line_text[0]) {
  429. join = true;
  430. line_is = 1;
  431. }
  432. }
  433. }
  434. if (style > 0) {
  435. if (join) {
  436. switch (p.border_style) {
  437. case door::BorderStyle::SINGLE:
  438. case door::BorderStyle::DOUBLE_SINGLE:
  439. border_is = 0;
  440. break;
  441. case door::BorderStyle::DOUBLE:
  442. case door::BorderStyle::SINGLE_DOUBLE:
  443. border_is = 1;
  444. break;
  445. default:
  446. break;
  447. }
  448. os << p.border_color;
  449. if (door::unicode)
  450. os << UJOIN[border_is][line_is][0]; // LEFT
  451. else
  452. os << JOIN[border_is][line_is][0]; // LEFT
  453. } else {
  454. os << p.border_color << s.side;
  455. };
  456. };
  457. // os << "[" << row << "," << p.x << "] ";
  458. os << *line;
  459. if (style > 0) {
  460. if (join) {
  461. os << p.border_color;
  462. if (door::unicode)
  463. os << UJOIN[border_is][line_is][1]; // RIGHT
  464. else
  465. os << JOIN[border_is][line_is][1]; // RIGHT
  466. } else
  467. os << p.border_color << s.side;
  468. };
  469. // os << "row " << row;
  470. row++;
  471. // forcing reset works. But why doesn't it work without this?
  472. // os << Color();
  473. }
  474. // Display bottom (if needed)
  475. if (style > 0) {
  476. os << door::Goto(p.x, row);
  477. os << p.border_color << s.bl;
  478. for (int c = 0; c < p.width; c++)
  479. os << s.top;
  480. // os << "";
  481. os << s.br;
  482. };
  483. // };
  484. // os << flush;
  485. return os;
  486. } // namespace door
  487. /*
  488. std::function<void(Door &d, std::string &)> Menu::defaultSelectedColorizer =
  489. Menu::makeColorizer(ANSIColor(COLOR::BLUE, COLOR::WHITE),
  490. ANSIColor(COLOR::BLUE, COLOR::WHITE),
  491. ANSIColor(COLOR::BLUE, COLOR::WHITE),
  492. ANSIColor(COLOR::BLUE, COLOR::WHITE));
  493. std::function<void(Door &d, std::string &)> Menu::defaultUnselectedColorizer
  494. = makeColorizer(ANSIColor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD),
  495. ANSIColor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD),
  496. ANSIColor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD),
  497. ANSIColor(COLOR::YELLOW, COLOR::BLUE, ATTR::BOLD));
  498. */
  499. renderFunction Menu::defaultSelectedRender = Menu::makeRender(
  500. ANSIColor(COLOR::BLUE, COLOR::WHITE), ANSIColor(COLOR::BLUE, COLOR::WHITE),
  501. ANSIColor(COLOR::BLUE, COLOR::WHITE), ANSIColor(COLOR::BLUE, COLOR::WHITE));
  502. renderFunction Menu::defaultUnselectedRender =
  503. Menu::makeRender(ANSIColor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD),
  504. ANSIColor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD),
  505. ANSIColor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD),
  506. ANSIColor(COLOR::YELLOW, COLOR::BLUE, ATTR::BOLD));
  507. /**
  508. * @brief Construct a new Menu object
  509. *
  510. * Set the x, y screen location for the start of the menu, and the width.
  511. * The location can be changed via \ref Panel::set
  512. *
  513. * @param x
  514. * @param y
  515. * @param width
  516. */
  517. Menu::Menu(int x, int y, int width) : Panel(x, y, width) {
  518. setStyle(BorderStyle::DOUBLE);
  519. // Setup initial sensible default values.
  520. // setColorizer(true, defaultSelectedColorizer);
  521. setRender(true, defaultSelectedRender);
  522. /* makeColorizer(Color(Colors::BLUE, Colors::WHITE, 0),
  523. Color(Colors::BLUE, Colors::WHITE, 0),
  524. Color(Colors::BLUE, Colors::WHITE, 0),
  525. Color(Colors::BLUE, Colors::WHITE, 0)));
  526. */
  527. setRender(false, defaultUnselectedRender);
  528. // setColorizer(false, defaultUnselectedColorizer);
  529. /* makeColorizer(Color(Colors::LWHITE, Colors::BLUE, 0),
  530. Color(Colors::LWHITE, Colors::BLUE),
  531. Color(Colors::LWHITE, Colors::BLUE, 0),
  532. Color(Colors::LYELLOW, Colors::BLUE)));
  533. */
  534. chosen = 0;
  535. }
  536. /**
  537. * @brief Construct a new Menu:: Menu object
  538. *
  539. * This creates a panel with a default width.
  540. * The location needs to be changed \ref Panel::set
  541. * @param width
  542. */
  543. Menu::Menu(int width) : Panel(width) {
  544. setStyle(BorderStyle::DOUBLE);
  545. setRender(true, defaultSelectedRender);
  546. setRender(false, defaultUnselectedRender);
  547. chosen = 0;
  548. }
  549. /*
  550. Menu::Menu(const Menu &original)
  551. : Panel(original.x, original.y, original.width) {
  552. x = original.x;
  553. y = original.y;
  554. width = original.width;
  555. setStyle(original.border_style);
  556. setRender(true, original.selectedRender);
  557. setRender(false, original.unselectedRender);
  558. options = original.options;
  559. chosen = 0;
  560. }
  561. */
  562. Menu::Menu(Menu &&ref) : Panel(ref.x, ref.y, ref.width) {
  563. x = ref.x;
  564. y = ref.y;
  565. width = ref.width;
  566. border_style = ref.border_style;
  567. setRender(true, ref.selectedRender);
  568. setRender(false, ref.unselectedRender);
  569. options = ref.options;
  570. lines = std::move(ref.lines);
  571. chosen = ref.chosen;
  572. }
  573. void Menu::addSelection(char c, const char *line) {
  574. std::string menuline;
  575. menuline.reserve(5 + strlen(line));
  576. menuline = "[ ] ";
  577. menuline[1] = c;
  578. menuline += line;
  579. // problem: How do I update the "Lines" from this point?
  580. // L->makeWidth(width);
  581. addLine(std::make_unique<Line>(menuline, width));
  582. options.push_back(c);
  583. }
  584. /**
  585. * @brief This allows for menus with updater functions
  586. *
  587. * Note: The update function only needs to update just the line text.
  588. * We wrap it with another updateFuntion to append the "[M] " part.
  589. *
  590. * @param c
  591. * @param line
  592. * @param update
  593. */
  594. void Menu::addSelection(char c, const char *line, updateFunction update) {
  595. std::string menuline;
  596. menuline.reserve(5 + strlen(line));
  597. menuline = "[ ] ";
  598. menuline[1] = c;
  599. updateFunction fullUpdate = [menuline, update](void) -> std::string {
  600. std::string text = menuline;
  601. text += update();
  602. return text;
  603. };
  604. menuline += line;
  605. std::unique_ptr<Line> l = std::make_unique<Line>(menuline, width);
  606. l->setUpdater(fullUpdate);
  607. // addLine(std::make_unique<Line>(menuline, width));
  608. addLine(std::move(l));
  609. options.push_back(c);
  610. }
  611. void Menu::defaultSelection(int d) { chosen = d; }
  612. char Menu::which(int d) { return options[d]; }
  613. /*
  614. void Menu::setColorizer(bool selected,
  615. std::function<void(Door &d, std::string &)>
  616. colorizer) { if (selected) selectedColorizer = colorizer; else
  617. unselectedColorizer = colorizer;
  618. }
  619. */
  620. void Menu::setRender(bool selected, renderFunction render) {
  621. if (selected)
  622. selectedRender = render;
  623. else
  624. unselectedRender = render;
  625. }
  626. /**
  627. * make Render function for menus
  628. *
  629. * [O] Menu Item Text
  630. *
  631. * "[" and "]" are in c1, the "O" in c2
  632. *
  633. * "Menu Item Text" upper case letters are in c3, everything else c4.
  634. */
  635. renderFunction Menu::makeRender(ANSIColor c1, ANSIColor c2, ANSIColor c3,
  636. ANSIColor c4) {
  637. renderFunction render = [c1, c2, c3, c4](const std::string &txt) -> Render {
  638. Render r(txt);
  639. bool option = true;
  640. for (char const &c : txt) {
  641. if (option) {
  642. if (c == '[' or c == ']') {
  643. r.append(c1);
  644. option = (c == '[');
  645. } else {
  646. r.append(c2);
  647. }
  648. } else {
  649. if (isupper(c))
  650. r.append(c3);
  651. else
  652. r.append(c4);
  653. }
  654. }
  655. return r;
  656. };
  657. return render;
  658. }
  659. /*
  660. Should this return the index number, or
  661. the actual option?
  662. */
  663. /**
  664. * @todo Fix this, so it only updates the lines that have been changed when
  665. * the user selects something. Also, add the "Up/Down Move" maybe to the
  666. * bottom?
  667. *
  668. * Needs timeout.
  669. *
  670. * Should we return the index offset, or return the actual char? (Like in
  671. * the case of Quit or Help?)
  672. *
  673. * @param door
  674. * @return int
  675. */
  676. int Menu::choose(Door &door) {
  677. // Display menu and make a choice
  678. // step 1: fix up the lines
  679. bool updated = true;
  680. bool update_and_exit = false;
  681. std::set<int> changed;
  682. door::ANSIColor blank(door::COLOR::BLACK); // , door::COLOR::BLACK);
  683. while (true) {
  684. if (updated) {
  685. for (unsigned int x = 0; x < lines.size(); ++x) {
  686. if (x == chosen) {
  687. lines[x]->setRender(
  688. selectedRender); // setColorize(selectedColorizer);
  689. } else {
  690. lines[x]->setRender(
  691. unselectedRender); // setColorize(unselectedColorizer);
  692. }
  693. }
  694. // this outputs the entire menu
  695. if (changed.empty())
  696. door << *this << blank;
  697. else {
  698. // update just the lines that have changed.
  699. for (auto si : changed) {
  700. // *this->update(door, si);
  701. update(door, si);
  702. }
  703. // Cursor is positioned at the end of the panel/menu.
  704. // The cursor changes colors as you arrow up or down.
  705. // Interesting!
  706. door << gotoEnd() << blank;
  707. }
  708. // door << flush;
  709. // door.update();
  710. };
  711. if (update_and_exit)
  712. return chosen + 1;
  713. // Maybe we want to position the cursor on the currently
  714. // selected item?
  715. updated = false;
  716. // Ok, when the option changes, can I control what gets updated??!?
  717. int event = door.sleep_key(door.inactivity);
  718. if (event < 0) {
  719. // timeout!
  720. return event;
  721. }
  722. unsigned int previous_choice = chosen;
  723. changed.clear();
  724. bool use_numberpad = true;
  725. // Don't use the numberpad, if any of the options are 8 or 2 (up/down)
  726. for (const char &c : options) {
  727. if ((c == '8') or (c == '2'))
  728. use_numberpad = false;
  729. }
  730. switch (event) {
  731. case '8':
  732. if (!use_numberpad)
  733. break;
  734. case XKEY_UP_ARROW:
  735. if (chosen > 0) {
  736. chosen--;
  737. updated = true;
  738. }
  739. break;
  740. case '2':
  741. if (!use_numberpad)
  742. break;
  743. case XKEY_DOWN_ARROW:
  744. if (chosen < lines.size() - 1) {
  745. chosen++;
  746. updated = true;
  747. }
  748. break;
  749. case XKEY_HOME:
  750. if (chosen != 0) {
  751. chosen = 0;
  752. updated = true;
  753. }
  754. break;
  755. case XKEY_END:
  756. if (chosen != lines.size() - 1) {
  757. chosen = lines.size() - 1;
  758. updated = true;
  759. }
  760. }
  761. if (event == 0x0d) {
  762. // ENTER -- use current selection
  763. return chosen + 1;
  764. }
  765. for (unsigned int x = 0; x < lines.size(); x++) {
  766. if (toupper(options[x]) == toupper(event)) {
  767. // is the selected one current chosen?
  768. if (chosen == x) {
  769. return x + 1;
  770. }
  771. // No, it isn't!
  772. // Update the screen, and then exit
  773. updated = true;
  774. chosen = x;
  775. update_and_exit = true;
  776. }
  777. }
  778. if (previous_choice != chosen) {
  779. changed.insert(previous_choice);
  780. changed.insert(chosen);
  781. }
  782. }
  783. return 0;
  784. }
  785. Screen::Screen(){}; // hidden = false; }
  786. /*
  787. Screen::Screen(Screen &s) {
  788. hidden = s.hidden;
  789. parts = s.parts;
  790. }
  791. */
  792. void Screen::addPanel(std::unique_ptr<Panel> p) {
  793. panels.push_back(std::move(p));
  794. }
  795. /*
  796. void Screen::hide(void) { hidden = true; }
  797. void Screen::show(void) { hidden = false; }
  798. */
  799. bool Screen::update(Door &d) {
  800. bool updated = false;
  801. for (auto &panel : panels) {
  802. if (panel->update(d))
  803. updated = true;
  804. }
  805. return updated;
  806. }
  807. void Screen::update(void) {
  808. for (auto &panel : panels) {
  809. panel->update();
  810. }
  811. }
  812. /**
  813. * @brief Outputs screen to stream.
  814. *
  815. * This iterates over panels, and outputs them.
  816. * See \ref door::Panel
  817. *
  818. * @param[in,out] os Stream
  819. * @param[in] s Screen to send
  820. * @return std::ostream&
  821. */
  822. std::ostream &operator<<(std::ostream &os, const Screen &s) {
  823. // if (!s.hidden) {
  824. for (auto &panel : s.panels) {
  825. os << *panel;
  826. };
  827. // os << flush;
  828. // }
  829. return os;
  830. }
  831. } // namespace door