ScrollingStatusBar.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #pragma once
  2. #include <helpers/ui/DisplayDriver.h>
  3. #ifndef STATUS_BAR_SCROLL_MS
  4. #define STATUS_BAR_SCROLL_MS 80
  5. #endif
  6. #ifndef STATUS_BAR_SEPARATOR
  7. #define STATUS_BAR_SEPARATOR " | "
  8. #endif
  9. #ifndef STATUS_BAR_UPDATE_MS
  10. #define STATUS_BAR_UPDATE_MS 2000 // rebuild status string every 2s
  11. #endif
  12. class ScrollingStatusBar {
  13. char _status[160];
  14. int _text_width;
  15. int _scroll_x;
  16. int _display_width;
  17. unsigned long _next_scroll;
  18. unsigned long _next_update;
  19. bool _needs_redraw;
  20. // cached state for change detection
  21. char _last_name[32];
  22. uint16_t _last_batt_mv;
  23. bool _last_buzzer_quiet;
  24. bool _last_gps_on;
  25. bool _last_ble_on;
  26. public:
  27. ScrollingStatusBar() : _text_width(0), _scroll_x(0), _display_width(72),
  28. _next_scroll(0), _next_update(0), _needs_redraw(true),
  29. _last_batt_mv(0), _last_buzzer_quiet(false),
  30. _last_gps_on(false), _last_ble_on(false) {
  31. _status[0] = 0;
  32. _last_name[0] = 0;
  33. }
  34. void begin(int display_width) {
  35. _display_width = display_width;
  36. _scroll_x = 0;
  37. _next_scroll = 0;
  38. _next_update = 0;
  39. }
  40. // Call periodically to update the status string content.
  41. // Only rebuilds if values have changed or update interval has elapsed.
  42. void update(DisplayDriver& display, const char* node_name, uint16_t batt_millivolts,
  43. bool buzzer_quiet, bool gps_on, bool ble_on) {
  44. bool changed = (batt_millivolts != _last_batt_mv)
  45. || (buzzer_quiet != _last_buzzer_quiet)
  46. || (gps_on != _last_gps_on)
  47. || (ble_on != _last_ble_on)
  48. || (strcmp(node_name, _last_name) != 0);
  49. if (!changed) return;
  50. // cache current values
  51. strncpy(_last_name, node_name, sizeof(_last_name) - 1);
  52. _last_name[sizeof(_last_name) - 1] = 0;
  53. _last_batt_mv = batt_millivolts;
  54. _last_buzzer_quiet = buzzer_quiet;
  55. _last_gps_on = gps_on;
  56. _last_ble_on = ble_on;
  57. float volts = batt_millivolts / 1000.0f;
  58. snprintf(_status, sizeof(_status),
  59. "%s" STATUS_BAR_SEPARATOR
  60. "%.2fV" STATUS_BAR_SEPARATOR
  61. "BUZ:%s" STATUS_BAR_SEPARATOR
  62. "GPS:%s" STATUS_BAR_SEPARATOR
  63. "BLE:%s"
  64. " - ", // trailing gap before the text loops
  65. node_name,
  66. volts,
  67. buzzer_quiet ? "OFF" : "ON",
  68. gps_on ? "ON" : "OFF",
  69. ble_on ? "ON" : "OFF"
  70. );
  71. display.setTextSize(1);
  72. _text_width = display.getTextWidth(_status);
  73. _next_update = millis() + STATUS_BAR_UPDATE_MS;
  74. _needs_redraw = true;
  75. }
  76. // Returns true if the status bar needs a redraw this frame.
  77. bool needsRedraw() {
  78. if (_text_width <= _display_width) return _needs_redraw; // static, no scrolling
  79. return millis() >= _next_scroll;
  80. }
  81. // Render the status bar via DisplayDriver.
  82. // U8g2 full-buffer mode clips to display bounds automatically,
  83. // and the font height stays within STATUS_BAR_HEIGHT, so no
  84. // explicit clip window is needed.
  85. void render(DisplayDriver& display) {
  86. if (_status[0] == 0) return;
  87. display.setTextSize(1);
  88. display.setColor(DisplayDriver::GREEN);
  89. // if (_needs_redraw) {
  90. // _text_width = display.getTextWidth(_status);
  91. // }
  92. // static text: no scrolling needed
  93. if (_text_width <= _display_width) {
  94. display.setCursor(0, 0);
  95. display.print(_status);
  96. _needs_redraw = false;
  97. return;
  98. }
  99. int x = _scroll_x;
  100. do {
  101. display.setCursor(x, 0);
  102. display.print(_status);
  103. x += _text_width;
  104. } while (x < _display_width);
  105. // advance scroll position
  106. _scroll_x--;
  107. if (_scroll_x <= -_text_width) _scroll_x = 0;
  108. _next_scroll = millis() + STATUS_BAR_SCROLL_MS;
  109. _needs_redraw = false;
  110. }
  111. };