A fork of pulp-os for the xteink4 adding custom apps
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: eliminate duplicates, fix constant magic numbers, simplify code

- Extract align_partial_region() helper to unify byte-alignment logic in
ssd1677 partial/full refresh paths.
- Collapse write_full_frame_async to delegate to write_full_frame.
- Extract read_averaged!() macro for 3x duplicate ADC oversampling.
- Add byte_to_char() helper to consolidate (0x20..=0x7E) patterns.
- Add FONT_SIZE_NAMES constant to unify Small/Medium/Large mappings.
- Fix sync_quick_menu to use reader::QA_FONT_SIZE instead of magic 1.
- Simplify ButtonMapper, collapse nested ifs, use div_ceil.
- Net: -59 lines, zero clippy warnings in pulp-os.

hans 44b18c1b 33a8c25a

+183 -242
+2 -6
src/apps/home.rs
··· 16 16 use crate::drivers::strip::StripBuffer; 17 17 use crate::fonts; 18 18 use crate::fonts::bitmap::BitmapFont; 19 + use crate::fonts::bitmap::byte_to_char; 19 20 use crate::ui::{Alignment, BUTTON_BAR_H, BitmapDynLabel, BitmapLabel, CONTENT_TOP, Region}; 20 21 21 22 // menu layout constants ··· 490 491 let mut ch_buf = [0u8; 8]; 491 492 let ch_len = fmt_chapter_prefix(&mut ch_buf, entry.chapter); 492 493 for &b in &ch_buf[..ch_len] { 493 - let ch = if (0x20..=0x7E).contains(&b) { 494 - b as char 495 - } else { 496 - '?' 497 - }; 498 - cx += font.draw_char_fg(strip, ch, fg, cx, baseline) as i32; 494 + cx += font.draw_char_fg(strip, byte_to_char(b), fg, cx, baseline) as i32; 499 495 } 500 496 } 501 497
+59 -63
src/apps/reader.rs
··· 5 5 6 6 extern crate alloc; 7 7 8 - use crate::fonts::bitmap::BitmapFont; 8 + use crate::fonts::bitmap::{self, BitmapFont}; 9 9 10 10 use alloc::vec; 11 11 use alloc::vec::Vec; ··· 80 80 81 81 const LOADING_REGION: Region = Region::new(MARGIN, TEXT_Y, 464, 20); 82 82 83 - const QA_FONT_SIZE: u8 = 1; 83 + pub const QA_FONT_SIZE: u8 = 1; 84 84 const QA_PREV_CHAPTER: u8 = 3; 85 85 const QA_NEXT_CHAPTER: u8 = 4; 86 86 const QA_TOC: u8 = 5; 87 87 88 - const QA_FONT_OPTIONS: &[&str] = &["Small", "Medium", "Large"]; 89 88 const QA_MAX: usize = 4; 90 89 91 90 pub const RECENT_FILE: &str = "RECENT"; ··· 320 319 QA_FONT_SIZE, 321 320 "Book Font", 322 321 self.book_font_size_idx, 323 - QA_FONT_OPTIONS, 322 + fonts::FONT_SIZE_NAMES, 324 323 ); 325 324 n += 1; 326 325 ··· 1182 1181 return true; 1183 1182 } 1184 1183 1185 - if self.is_epub && self.fully_indexed { 1186 - if (self.chapter as usize + 1) < self.spine.len() { 1187 - self.chapter += 1; 1188 - self.goto_last_page = false; 1189 - self.state = State::NeedIndex; 1190 - return true; 1191 - } 1184 + if self.is_epub && self.fully_indexed && (self.chapter as usize + 1) < self.spine.len() { 1185 + self.chapter += 1; 1186 + self.goto_last_page = false; 1187 + self.state = State::NeedIndex; 1188 + return true; 1192 1189 } 1193 1190 1194 1191 false ··· 1574 1571 if width == 0 || height == 0 { 1575 1572 return Err("zero dimensions in cache"); 1576 1573 } 1577 - let stride = (width as usize + 7) / 8; 1574 + let stride = (width as usize).div_ceil(8); 1578 1575 let data_len = stride * height as usize; 1579 1576 if size as usize != 4 + data_len { 1580 1577 return Err("cache size mismatch"); ··· 2303 2300 j += 2; 2304 2301 continue; 2305 2302 } 2306 - let ch = if (0x20..=0x7E).contains(&b) { 2307 - b as char 2308 - } else { 2303 + if !(bitmap::FIRST_CHAR..=bitmap::LAST_CHAR).contains(&b) { 2309 2304 j += 1; 2310 2305 continue; // non-printable 2311 - }; 2312 - cx += fs.draw_char(strip, ch, sty, cx, baseline) as i32; 2306 + } 2307 + cx += fs.draw_char(strip, b as char, sty, cx, baseline) as i32; 2313 2308 j += 1; 2314 2309 } 2315 2310 } ··· 2343 2338 } 2344 2339 2345 2340 // position overlay (long-press feedback) 2346 - if self.show_position && self.state == State::Ready { 2347 - if POSITION_OVERLAY.intersects(strip.logical_window()) { 2348 - let mut pbuf = StackFmt::<48>::new(); 2349 - if self.is_epub && self.spine.len() > 1 { 2350 - if self.fully_indexed { 2351 - let _ = write!( 2352 - pbuf, 2353 - "Ch {}/{} Page {}/{}", 2354 - self.chapter + 1, 2355 - self.spine.len(), 2356 - self.page + 1, 2357 - self.total_pages 2358 - ); 2359 - } else { 2360 - let _ = write!( 2361 - pbuf, 2362 - "Ch {}/{} Page {}", 2363 - self.chapter + 1, 2364 - self.spine.len(), 2365 - self.page + 1 2366 - ); 2367 - } 2368 - } else if self.fully_indexed { 2369 - let _ = write!(pbuf, "Page {}/{}", self.page + 1, self.total_pages); 2341 + if self.show_position 2342 + && self.state == State::Ready 2343 + && POSITION_OVERLAY.intersects(strip.logical_window()) 2344 + { 2345 + let mut pbuf = StackFmt::<48>::new(); 2346 + if self.is_epub && self.spine.len() > 1 { 2347 + if self.fully_indexed { 2348 + let _ = write!( 2349 + pbuf, 2350 + "Ch {}/{} Page {}/{}", 2351 + self.chapter + 1, 2352 + self.spine.len(), 2353 + self.page + 1, 2354 + self.total_pages 2355 + ); 2370 2356 } else { 2371 - let _ = write!(pbuf, "Page {} ({}%)", self.page + 1, self.progress_pct()); 2357 + let _ = write!( 2358 + pbuf, 2359 + "Ch {}/{} Page {}", 2360 + self.chapter + 1, 2361 + self.spine.len(), 2362 + self.page + 1 2363 + ); 2372 2364 } 2365 + } else if self.fully_indexed { 2366 + let _ = write!(pbuf, "Page {}/{}", self.page + 1, self.total_pages); 2367 + } else { 2368 + let _ = write!(pbuf, "Page {} ({}%)", self.page + 1, self.progress_pct()); 2369 + } 2373 2370 2374 - // inverted banner: black bg, white text 2375 - POSITION_OVERLAY 2376 - .to_rect() 2377 - .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) 2371 + // inverted banner: black bg, white text 2372 + POSITION_OVERLAY 2373 + .to_rect() 2374 + .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)) 2375 + .draw(strip) 2376 + .unwrap(); 2377 + let text = pbuf.as_str(); 2378 + if let Some(f) = cf { 2379 + f.draw_aligned( 2380 + strip, 2381 + POSITION_OVERLAY, 2382 + text, 2383 + Alignment::Center, 2384 + BinaryColor::Off, 2385 + ); 2386 + } else { 2387 + let tw = text.len() as u32 * 6; 2388 + let pos = Alignment::Center.position(POSITION_OVERLAY, Size::new(tw, 13)); 2389 + let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::Off); 2390 + Text::new(text, Point::new(pos.x, pos.y + 13), style) 2378 2391 .draw(strip) 2379 2392 .unwrap(); 2380 - let text = pbuf.as_str(); 2381 - if let Some(f) = cf { 2382 - f.draw_aligned( 2383 - strip, 2384 - POSITION_OVERLAY, 2385 - text, 2386 - Alignment::Center, 2387 - BinaryColor::Off, 2388 - ); 2389 - } else { 2390 - let tw = text.len() as u32 * 6; 2391 - let pos = Alignment::Center.position(POSITION_OVERLAY, Size::new(tw, 13)); 2392 - let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::Off); 2393 - Text::new(text, Point::new(pos.x, pos.y + 13), style) 2394 - .draw(strip) 2395 - .unwrap(); 2396 - } 2397 2393 } 2398 2394 } 2399 2395 }
+10 -12
src/apps/settings.rs
··· 367 367 let _ = write!(buf, "Every {}", self.settings.ghost_clear_every); 368 368 } 369 369 2 => { 370 - let s = match self.settings.book_font_size_idx { 371 - 1 => "Medium", 372 - 2 => "Large", 373 - _ => "Small", 374 - }; 375 - let _ = write!(buf, "{}", s); 370 + let _ = write!( 371 + buf, 372 + "{}", 373 + fonts::font_size_name(self.settings.book_font_size_idx) 374 + ); 376 375 } 377 376 3 => { 378 - let s = match self.settings.ui_font_size_idx { 379 - 1 => "Medium", 380 - 2 => "Large", 381 - _ => "Small", 382 - }; 383 - let _ = write!(buf, "{}", s); 377 + let _ = write!( 378 + buf, 379 + "{}", 380 + fonts::font_size_name(self.settings.ui_font_size_idx) 381 + ); 384 382 } 385 383 _ => {} 386 384 }
+2 -2
src/bin/main.rs
··· 23 23 24 24 use pulp_os::apps::files::FilesApp; 25 25 use pulp_os::apps::home::HomeApp; 26 - use pulp_os::apps::reader::ReaderApp; 26 + use pulp_os::apps::reader::{self, ReaderApp}; 27 27 use pulp_os::apps::settings::SettingsApp; 28 28 use pulp_os::apps::{ 29 29 App, AppContext, AppId, BookmarkCache, Launcher, Redraw, Services, Transition, ··· 731 731 732 732 // persist reader font-size change into settings 733 733 if active == AppId::Reader 734 - && let Some(font_idx) = qm.app_cycle_value(1) 734 + && let Some(font_idx) = qm.app_cycle_value(reader::QA_FONT_SIZE) 735 735 { 736 736 let ss = apps.settings.system_settings_mut(); 737 737 if ss.book_font_size_idx != font_idx {
+3 -10
src/board/action.rs
··· 44 44 } 45 45 } 46 46 47 - pub struct ButtonMapper { 48 - _private: (), 49 - } 50 - 51 - impl Default for ButtonMapper { 52 - fn default() -> Self { 53 - Self::new() 54 - } 55 - } 47 + #[derive(Default)] 48 + pub struct ButtonMapper; 56 49 57 50 impl ButtonMapper { 58 51 pub const fn new() -> Self { 59 - Self { _private: () } 52 + Self 60 53 } 61 54 62 55 pub fn map_button(&self, button: Button) -> Action {
+14 -15
src/drivers/input.rs
··· 9 9 use crate::board::InputHw; 10 10 use crate::board::button::{Button, ROW1_THRESHOLDS, ROW2_THRESHOLDS, decode_ladder}; 11 11 12 + // 4-sample average; ~40us per channel, rejects ADC noise from SPI/battery sag 13 + macro_rules! read_averaged { 14 + ($adc:expr, $pin:expr) => {{ 15 + let mut sum: u32 = 0; 16 + for _ in 0..ADC_OVERSAMPLE { 17 + sum += nb::block!($adc.read_oneshot($pin)).unwrap() as u32; 18 + } 19 + (sum / ADC_OVERSAMPLE) as u16 20 + }}; 21 + } 22 + 12 23 const DEBOUNCE_MS: u64 = 15; 13 24 const LONG_PRESS_MS: u64 = 1000; 14 25 const REPEAT_MS: u64 = 150; ··· 144 155 } 145 156 146 157 fn read_averaged_row1(&mut self) -> u16 { 147 - let mut sum: u32 = 0; 148 - for _ in 0..ADC_OVERSAMPLE { 149 - sum += nb::block!(self.hw.adc.read_oneshot(&mut self.hw.row1)).unwrap() as u32; 150 - } 151 - (sum / ADC_OVERSAMPLE) as u16 158 + read_averaged!(self.hw.adc, &mut self.hw.row1) 152 159 } 153 160 154 161 fn read_averaged_row2(&mut self) -> u16 { 155 - let mut sum: u32 = 0; 156 - for _ in 0..ADC_OVERSAMPLE { 157 - sum += nb::block!(self.hw.adc.read_oneshot(&mut self.hw.row2)).unwrap() as u32; 158 - } 159 - (sum / ADC_OVERSAMPLE) as u16 162 + read_averaged!(self.hw.adc, &mut self.hw.row2) 160 163 } 161 164 162 165 pub fn read_battery_mv(&mut self) -> u16 { 163 - let mut sum: u32 = 0; 164 - for _ in 0..ADC_OVERSAMPLE { 165 - sum += nb::block!(self.hw.adc.read_oneshot(&mut self.hw.battery)).unwrap() as u32; 166 - } 167 - (sum / ADC_OVERSAMPLE) as u16 166 + read_averaged!(self.hw.adc, &mut self.hw.battery) 168 167 } 169 168 }
-1
src/drivers/sdcard.rs
··· 4 4 use embedded_sdmmc::{SdCard, TimeSource, Timestamp, VolumeManager}; 5 5 use log::info; 6 6 7 - // No RTC on board; all file timestamps are set to 2025-01-01 00:00:00. 8 7 #[derive(Default, Clone, Copy)] 9 8 pub struct FixedTimestamp; 10 9
+69 -108
src/drivers/ssd1677.rs
··· 292 292 } 293 293 } 294 294 295 + // transform logical region to byte-aligned physical coords + edge masks 296 + fn align_partial_region(&self, x: u16, y: u16, w: u16, h: u16) -> Option<RenderState> { 297 + let (tx, ty, tw, th) = self.transform_region(x, y, w, h); 298 + 299 + let px = (tx & !7).min(WIDTH); 300 + let py = ty.min(HEIGHT); 301 + let pw = ((tw + (tx & 7) + 7) & !7).min(WIDTH - px); 302 + let ph = th.min(HEIGHT - py); 303 + 304 + if pw == 0 || ph == 0 { 305 + return None; 306 + } 307 + 308 + let lp = (tx - px) as u32; 309 + let rp = ((px + pw) - (tx + tw)) as u32; 310 + let left_mask: u8 = if lp > 0 { !((1u8 << (8 - lp)) - 1) } else { 0 }; 311 + let right_mask: u8 = if rp > 0 { (1u8 << rp) - 1 } else { 0 }; 312 + 313 + Some(RenderState { 314 + px, 315 + py, 316 + pw, 317 + ph, 318 + left_mask, 319 + right_mask, 320 + }) 321 + } 322 + 295 323 // gates wired in reverse; Y flipped per GxEPD2 296 324 fn set_partial_ram_area(&mut self, x: u16, y: u16, w: u16, h: u16) { 297 325 let y_flipped = HEIGHT - y - h; ··· 380 408 if self.initial_refresh { 381 409 return None; 382 410 } 383 - 384 411 if !self.init_done { 385 412 self.init_display(delay); 386 413 } 387 414 388 - let (tx, ty, tw, th) = self.transform_region(x, y, w, h); 389 - 390 - let px = (tx & !7).min(WIDTH); 391 - let py = ty.min(HEIGHT); 392 - let pw = ((tw + (tx & 7) + 7) & !7).min(WIDTH - px); 393 - let ph = th.min(HEIGHT - py); 394 - 395 - if pw == 0 || ph == 0 { 396 - return None; 397 - } 398 - 399 - let lp = (tx - px) as u32; 400 - let rp = ((px + pw) - (tx + tw)) as u32; 401 - let left_mask: u8 = if lp > 0 { !((1u8 << (8 - lp)) - 1) } else { 0 }; 402 - let right_mask: u8 = if rp > 0 { (1u8 << rp) - 1 } else { 0 }; 403 - 415 + let rs = self.align_partial_region(x, y, w, h)?; 404 416 self.write_region_strips( 405 417 strip, 406 - px, 407 - py, 408 - pw, 409 - ph, 418 + rs.px, 419 + rs.py, 420 + rs.pw, 421 + rs.ph, 410 422 cmd::WRITE_RAM_BW, 411 423 draw, 412 - left_mask, 413 - right_mask, 424 + rs.left_mask, 425 + rs.right_mask, 414 426 ); 415 - 416 - Some(RenderState { 417 - px, 418 - py, 419 - pw, 420 - ph, 421 - left_mask, 422 - right_mask, 423 - }) 427 + Some(rs) 424 428 } 425 429 426 430 // write BW + inverted RED; use after skipped phase 3 to fix stale RED ··· 441 445 if self.initial_refresh { 442 446 return None; 443 447 } 444 - 445 448 if !self.init_done { 446 449 self.init_display(delay); 447 450 } 448 451 449 - let (tx, ty, tw, th) = self.transform_region(x, y, w, h); 450 - 451 - let px = (tx & !7).min(WIDTH); 452 - let py = ty.min(HEIGHT); 453 - let pw = ((tw + (tx & 7) + 7) & !7).min(WIDTH - px); 454 - let ph = th.min(HEIGHT - py); 455 - 456 - if pw == 0 || ph == 0 { 457 - return None; 458 - } 459 - 460 - let lp = (tx - px) as u32; 461 - let rp = ((px + pw) - (tx + tw)) as u32; 462 - let left_mask: u8 = if lp > 0 { !((1u8 << (8 - lp)) - 1) } else { 0 }; 463 - let right_mask: u8 = if rp > 0 { (1u8 << rp) - 1 } else { 0 }; 464 - 465 - self.write_region_strips_bw_inv_red(strip, px, py, pw, ph, draw, left_mask, right_mask); 466 - 467 - Some(RenderState { 468 - px, 469 - py, 470 - pw, 471 - ph, 472 - left_mask, 473 - right_mask, 474 - }) 452 + let rs = self.align_partial_region(x, y, w, h)?; 453 + self.write_region_strips_bw_inv_red( 454 + strip, 455 + rs.px, 456 + rs.py, 457 + rs.pw, 458 + rs.ph, 459 + draw, 460 + rs.left_mask, 461 + rs.right_mask, 462 + ); 463 + Some(rs) 475 464 } 476 465 477 466 // kick DU waveform (non-blocking); caller polls is_busy ··· 598 587 let _ = self.busy.wait_for_low().await; 599 588 } 600 589 601 - // async write_full_frame; SPI writes still blocking (DMA) 590 + // async write_full_frame; SPI writes are blocking (DMA), no .await needed 602 591 pub async fn write_full_frame_async<F>( 603 592 &mut self, 604 593 strip: &mut StripBuffer, ··· 607 596 ) where 608 597 F: Fn(&mut StripBuffer), 609 598 { 610 - if !self.init_done { 611 - self.init_display(delay); 612 - } 613 - 614 - delay.delay_millis(1); 615 - 616 - for &ram_cmd in &[cmd::WRITE_RAM_RED, cmd::WRITE_RAM_BW] { 617 - self.set_partial_ram_area(0, 0, WIDTH, HEIGHT); 618 - self.send_command(ram_cmd); 619 - delay.delay_millis(1); 620 - 621 - for i in 0..STRIP_COUNT { 622 - strip.begin_strip(self.rotation, i); 623 - draw(strip); 624 - self.send_data(strip.data()); 625 - } 626 - } 599 + self.write_full_frame(strip, delay, draw); 627 600 } 628 601 629 602 // partial DU refresh in one await (~500ms); falls back to full GC on initial_refresh. ··· 644 617 self.full_refresh_async(strip, delay, draw).await; 645 618 return; 646 619 } 647 - 648 620 if !self.init_done { 649 621 self.init_display(delay); 650 622 } 651 623 652 - let (tx, ty, tw, th) = self.transform_region(x, y, w, h); 653 - 654 - let px = (tx & !7).min(WIDTH); 655 - let py = ty.min(HEIGHT); 656 - let pw = ((tw + (tx & 7) + 7) & !7).min(WIDTH - px); 657 - let ph = th.min(HEIGHT - py); 658 - 659 - if pw == 0 || ph == 0 { 660 - return; 661 - } 662 - 663 - let lp = (tx - px) as u32; 664 - let rp = ((px + pw) - (tx + tw)) as u32; 665 - let left_mask: u8 = if lp > 0 { !((1u8 << (8 - lp)) - 1) } else { 0 }; 666 - let right_mask: u8 = if rp > 0 { (1u8 << rp) - 1 } else { 0 }; 667 - 668 - let rs = RenderState { 669 - px, 670 - py, 671 - pw, 672 - ph, 673 - left_mask, 674 - right_mask, 624 + let rs = match self.align_partial_region(x, y, w, h) { 625 + Some(rs) => rs, 626 + None => return, 675 627 }; 676 628 677 629 // phase 1: write BW RAM 678 630 self.write_region_strips( 679 631 strip, 680 - px, 681 - py, 682 - pw, 683 - ph, 632 + rs.px, 633 + rs.py, 634 + rs.pw, 635 + rs.ph, 684 636 cmd::WRITE_RAM_BW, 685 637 draw, 686 - left_mask, 687 - right_mask, 638 + rs.left_mask, 639 + rs.right_mask, 688 640 ); 689 641 690 642 // phase 2: kick DU waveform and await completion ··· 692 644 self.wait_busy_async().await; 693 645 694 646 // phase 3: sync both RAM planes 695 - self.write_region_strips_dual(strip, px, py, pw, ph, draw, left_mask, right_mask); 647 + self.write_region_strips_dual( 648 + strip, 649 + rs.px, 650 + rs.py, 651 + rs.pw, 652 + rs.ph, 653 + draw, 654 + rs.left_mask, 655 + rs.right_mask, 656 + ); 696 657 697 658 self.power_off_async().await; 698 659 }
+1 -5
src/drivers/storage.rs
··· 259 259 if space == 0 { 260 260 // line longer than buffer; skip to next newline 261 261 leftover = 0; 262 - loop { 263 - let n = match read_pulp_file_chunk(sd, TITLES_FILE, offset, &mut buf) { 264 - Ok(n) => n, 265 - Err(_) => break, 266 - }; 262 + while let Ok(n) = read_pulp_file_chunk(sd, TITLES_FILE, offset, &mut buf) { 267 263 if n == 0 { 268 264 break; 269 265 }
+12 -14
src/fonts/bitmap.rs
··· 11 11 pub const LAST_CHAR: u8 = 0x7E; 12 12 pub const GLYPH_COUNT: usize = (LAST_CHAR - FIRST_CHAR + 1) as usize; // 95 13 13 14 + // map arbitrary byte to printable char; out-of-range becomes '?' 15 + #[inline] 16 + pub fn byte_to_char(b: u8) -> char { 17 + if (FIRST_CHAR..=LAST_CHAR).contains(&b) { 18 + b as char 19 + } else { 20 + '?' 21 + } 22 + } 23 + 14 24 #[derive(Clone, Copy)] 15 25 #[repr(C)] 16 26 pub struct BitmapGlyph { ··· 55 65 #[inline] 56 66 pub fn measure_bytes(&self, text: &[u8]) -> u16 { 57 67 text.iter() 58 - .map(|&b| { 59 - let ch = if (0x20..=0x7E).contains(&b) { 60 - b as char 61 - } else { 62 - '?' 63 - }; 64 - self.advance(ch) as u16 65 - }) 68 + .map(|&b| self.advance(byte_to_char(b)) as u16) 66 69 .sum() 67 70 } 68 71 ··· 113 116 pub fn draw_bytes(&self, strip: &mut StripBuffer, text: &[u8], cx: i32, baseline: i32) -> i32 { 114 117 let mut x = cx; 115 118 for &b in text { 116 - let ch = if (0x20..=0x7E).contains(&b) { 117 - b as char 118 - } else { 119 - '?' 120 - }; 121 - x += self.draw_char(strip, ch, x, baseline) as i32; 119 + x += self.draw_char(strip, byte_to_char(b), x, baseline) as i32; 122 120 } 123 121 x 124 122 }
+11 -6
src/fonts/mod.rs
··· 12 12 use crate::drivers::strip::StripBuffer; 13 13 use bitmap::BitmapFont; 14 14 15 + // 0 = Small, 1 = Medium, 2 = Large 16 + pub const FONT_SIZE_NAMES: &[&str] = &["Small", "Medium", "Large"]; 17 + 18 + pub fn font_size_name(idx: u8) -> &'static str { 19 + FONT_SIZE_NAMES 20 + .get(idx as usize) 21 + .copied() 22 + .unwrap_or("Small") 23 + } 24 + 15 25 // body font by index: 0 = Small, 1 = Medium, 2 = Large 16 26 pub fn body_font(idx: u8) -> &'static BitmapFont { 17 27 match idx { ··· 136 146 137 147 #[inline] 138 148 pub fn advance_byte(&self, b: u8, style: Style) -> u8 { 139 - let ch = if (0x20..=0x7E).contains(&b) { 140 - b as char 141 - } else { 142 - '?' 143 - }; 144 - self.font(style).advance(ch) 149 + self.font(style).advance(bitmap::byte_to_char(b)) 145 150 } 146 151 147 152 #[inline]