feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+182
@@ -0,0 +1,182 @@
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use crate::{Array, Item, Table};
|
||||
|
||||
/// A top-level sequence of [`Table`]s, each under their own header
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ArrayOfTables {
|
||||
// Always Vec<Item::Table>, just `Item` to make `Index` work
|
||||
pub(crate) span: Option<std::ops::Range<usize>>,
|
||||
pub(crate) values: Vec<Item>,
|
||||
}
|
||||
|
||||
/// Constructors
|
||||
///
|
||||
/// See also `FromIterator`
|
||||
impl ArrayOfTables {
|
||||
/// Creates an empty array of tables.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Formatting
|
||||
impl ArrayOfTables {
|
||||
/// Convert to an inline array
|
||||
pub fn into_array(mut self) -> Array {
|
||||
for value in self.values.iter_mut() {
|
||||
value.make_value();
|
||||
}
|
||||
let mut a = Array::with_vec(self.values);
|
||||
a.fmt();
|
||||
a
|
||||
}
|
||||
|
||||
/// The location within the original document
|
||||
///
|
||||
/// This generally requires a [`Document`][crate::Document].
|
||||
pub fn span(&self) -> Option<std::ops::Range<usize>> {
|
||||
self.span.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn despan(&mut self, input: &str) {
|
||||
self.span = None;
|
||||
for value in &mut self.values {
|
||||
value.despan(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayOfTables {
|
||||
/// Returns an iterator over tables.
|
||||
pub fn iter(&self) -> ArrayOfTablesIter<'_> {
|
||||
Box::new(self.values.iter().filter_map(Item::as_table))
|
||||
}
|
||||
|
||||
/// Returns an iterator over tables.
|
||||
pub fn iter_mut(&mut self) -> ArrayOfTablesIterMut<'_> {
|
||||
Box::new(self.values.iter_mut().filter_map(Item::as_table_mut))
|
||||
}
|
||||
|
||||
/// Returns the length of the underlying Vec.
|
||||
/// To get the actual number of items use `a.iter().count()`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// Returns true if `self.len() == 0`.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Removes all the tables.
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
}
|
||||
|
||||
/// Returns an optional reference to the table.
|
||||
pub fn get(&self, index: usize) -> Option<&Table> {
|
||||
self.values.get(index).and_then(Item::as_table)
|
||||
}
|
||||
|
||||
/// Returns an optional mutable reference to the table.
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut Table> {
|
||||
self.values.get_mut(index).and_then(Item::as_table_mut)
|
||||
}
|
||||
|
||||
/// Appends a table to the array.
|
||||
pub fn push(&mut self, table: Table) {
|
||||
self.values.push(Item::Table(table));
|
||||
}
|
||||
|
||||
/// Inserts a table at the given index within the array, shifting all
|
||||
/// tables after it to the right.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index > len`.
|
||||
pub fn insert(&mut self, index: usize, table: Table) {
|
||||
self.values.insert(index, Item::Table(table));
|
||||
}
|
||||
|
||||
/// Removes a table with the given index.
|
||||
pub fn remove(&mut self, index: usize) -> Table {
|
||||
self.values
|
||||
.remove(index)
|
||||
.into_table()
|
||||
.expect("cannot have any other item in an array-of-tables")
|
||||
}
|
||||
|
||||
/// Retains only the elements specified by the `keep` predicate.
|
||||
///
|
||||
/// In other words, remove all tables for which `keep(&table)` returns `false`.
|
||||
///
|
||||
/// This method operates in place, visiting each element exactly once in the
|
||||
/// original order, and preserves the order of the retained elements.
|
||||
pub fn retain<F>(&mut self, mut keep: F)
|
||||
where
|
||||
F: FnMut(&Table) -> bool,
|
||||
{
|
||||
self.values
|
||||
.retain(|item| item.as_table().map(&mut keep).unwrap_or(false));
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
|
||||
pub type ArrayOfTablesIter<'a> = Box<dyn Iterator<Item = &'a Table> + 'a>;
|
||||
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
|
||||
pub type ArrayOfTablesIterMut<'a> = Box<dyn Iterator<Item = &'a mut Table> + 'a>;
|
||||
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
|
||||
pub type ArrayOfTablesIntoIter = Box<dyn Iterator<Item = Table>>;
|
||||
|
||||
impl Extend<Table> for ArrayOfTables {
|
||||
fn extend<T: IntoIterator<Item = Table>>(&mut self, iter: T) {
|
||||
for value in iter {
|
||||
self.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Table> for ArrayOfTables {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = Table>,
|
||||
{
|
||||
let v = iter.into_iter().map(Item::Table);
|
||||
Self {
|
||||
values: v.collect(),
|
||||
span: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for ArrayOfTables {
|
||||
type Item = Table;
|
||||
type IntoIter = ArrayOfTablesIntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(
|
||||
self.values
|
||||
.into_iter()
|
||||
.filter(|v| v.is_table())
|
||||
.map(|v| v.into_table().unwrap()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> IntoIterator for &'s ArrayOfTables {
|
||||
type Item = &'s Table;
|
||||
type IntoIter = ArrayOfTablesIter<'s>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "display")]
|
||||
impl std::fmt::Display for ArrayOfTables {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// HACK: Without the header, we don't really have a proper way of printing this
|
||||
self.clone().into_array().fmt(f)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user