⭐
Log in to mark this repository.
tui: show and hide kinds of dates on the calendar tab
0f89366 · Gabriel Morell · 2026-10-19 16:35
✓
Rust CI and Docker Build / Create Release (push)
✓
Rust CI and Docker Build / Release (7z a, .exe, ppl-x86_64-windows.zip, windows-latest, windows-latest, 1.92) (push)
✓
Rust CI and Docker Build / Release (tar -czf, , ppl-aarch64-linux-gnu.tar.gz, ubuntu-24.04-arm, ubuntu-24.04-arm, 1.92) (push)
✓
Rust CI and Docker Build / Release (tar -czf, , ppl-aarch64-macos.tar.gz, macos-latest, macos-latest, 1.92) (push)
✓
Rust CI and Docker Build / Release (tar -czf, , ppl-x86_64-linux-gnu.tar.gz, ubuntu-latest, arc-runner-set, 1.92) (push)
✗
Rust CI and Docker Build / build (aarch64, ubuntu-24.04-arm) (push)
✓
Rust CI and Docker Build / build (x86_64, arc-runner-set) (push)
Message
{commit_body(@commit)}
Files changed
modified
README.md
+4
−1
@@ -77,7 +77,10 @@ While editing a person, `Delete` (or backspace) twice removes the selected row.
| 77 | 77 | |
| 78 | 78 | Upcoming anniversaries and holidays for the year; `↑`/`↓` scroll the upcoming list, which opens on today, |
| 79 | 79 | and the selected day is highlighted in white in the month grid. Scrolling past the end of the year |
| 80 | −carries on into the next (and above the start, into the last); `PgUp`/`PgDn` jump a year. Press `i` on a holiday for a short | |
| 80 | +carries on into the next (and above the start, into the last); `PgUp`/`PgDn` jump a year. | |
| 81 | +Like the ppl list's toggles, `n` (countries), `r` (religious), `m` (moon and fiscal), `s` (subscribed) | |
| 82 | +and `p` (people's dates) show or hide those dates on the calendar tab; hidden ones are struck | |
| 83 | +through in the footer. Press `i` on a holiday for a short | |
| 81 | 84 | description and a Wikipedia link (`o` opens it in your browser). |
| 82 | 85 | |
| 83 | 86 | Press `c` to switch built-in calendars on and off. Their holidays show in the upcoming list and |
modified
src/do_tui.rs
+83
−25
@@ -7,7 +7,7 @@ use crate::entities::ppl::Model;
| 7 | 7 | use crate::entities::{ |
| 8 | 8 | calendar, contact, ppl, relation, sig_date, tier, tier_defaults, trait_defaults, traits, |
| 9 | 9 | }; |
| 10 | −use crate::holidays::{holidays_in, Holiday}; | |
| 10 | +use crate::holidays::{group_of, holidays_in, Holiday}; | |
| 11 | 11 | use crate::tui_add::{render_person_menu, AddContext, AddForm, AddKind, FormAction}; |
| 12 | 12 | use crate::tui_calendars::{ |
| 13 | 13 | holiday_color, open_url, render_holiday_info, CalendarMenu, MenuAction, |
@@ -82,6 +82,8 @@ struct Tui {
| 82 | 82 | holiday_info: Option<Holiday>, |
| 83 | 83 | /// The year the calendar tab shows; scrolling past either end moves it |
| 84 | 84 | cal_year: i32, |
| 85 | + /// Holiday groups (by key) and "people" hidden on the calendar tab | |
| 86 | + cal_hidden: Vec<&'static str>, | |
| 85 | 87 | } |
| 86 | 88 | |
| 87 | 89 | #[derive(Debug, PartialEq, Sequence, Clone)] |
@@ -213,6 +215,7 @@ impl Default for Tui {
| 213 | 215 | upcoming_holidays: vec![], |
| 214 | 216 | holiday_info: None, |
| 215 | 217 | cal_year: Local::now().year(), |
| 218 | + cal_hidden: vec![], | |
| 216 | 219 | } |
| 217 | 220 | } |
| 218 | 221 | } |
@@ -428,6 +431,21 @@ pub async fn run_tui(
| 428 | 431 | KeyCode::Char(_) if app.current_tab == Tabs::Ppl && app.ppl_field_editing => { |
| 429 | 432 | default_editing_handler(&mut app, &key_event); |
| 430 | 433 | } |
| 434 | + // Show or hide a kind of date on the calendar tab, like the ppl tab's toggles | |
| 435 | + KeyCode::Char(c) | |
| 436 | + if app.current_tab == Tabs::Calendar && cal_toggle(c).is_some() => | |
| 437 | + { | |
| 438 | + if let Some(what) = cal_toggle(c) { | |
| 439 | + match app.cal_hidden.iter().position(|h| *h == what) { | |
| 440 | + Some(i) => { | |
| 441 | + app.cal_hidden.remove(i); | |
| 442 | + } | |
| 443 | + None => app.cal_hidden.push(what), | |
| 444 | + } | |
| 445 | + // The list changed under the cursor; start again from today | |
| 446 | + app.sigdate_cal_state = ListState::default(); | |
| 447 | + } | |
| 448 | + } | |
| 431 | 449 | KeyCode::Char('e') => { |
| 432 | 450 | if app.current_tab == Tabs::Ppl { |
| 433 | 451 | match app.ppl_field_editing { |
@@ -602,6 +620,22 @@ pub async fn run_tui(
| 602 | 620 | } |
| 603 | 621 | } |
| 604 | 622 | |
| 623 | +/// What a key shows or hides on the calendar tab: a holiday group's key, or "people" | |
| 624 | +const CAL_TOGGLES: [(char, &str, &str); 5] = [ | |
| 625 | + ('n', "countries", "countries"), | |
| 626 | + ('r', "religious", "religious"), | |
| 627 | + ('m', "other", "moon/fiscal"), | |
| 628 | + ('s', "subscribed", "subscribed"), | |
| 629 | + ('p', "people", "people"), | |
| 630 | +]; | |
| 631 | + | |
| 632 | +fn cal_toggle(key: char) -> Option<&'static str> { | |
| 633 | + CAL_TOGGLES | |
| 634 | + .iter() | |
| 635 | + .find(|(k, _, _)| *k == key) | |
| 636 | + .map(|(_, what, _)| *what) | |
| 637 | +} | |
| 638 | + | |
| 605 | 639 | /// The tier row the reminder keys act on: a tier default on the tiers tab, |
| 606 | 640 | /// or the selected tier of a person being edited on the ppl tab |
| 607 | 641 | struct ReminderTarget { |
@@ -1528,7 +1562,13 @@ fn render(f: &mut Frame, app: &mut Tui) {
| 1528 | 1562 | ); |
| 1529 | 1563 | |
| 1530 | 1564 | // Holidays first, so a person's date on the same day wins the colour |
| 1531 | − for h in &app.holidays { | |
| 1565 | + let shown = app | |
| 1566 | + .holidays | |
| 1567 | + .iter() | |
| 1568 | + .filter(|h| !app.cal_hidden.contains(&group_of(h.calendar).key)) | |
| 1569 | + .collect::<Vec<_>>(); | |
| 1570 | + let show_people = !app.cal_hidden.contains(&"people"); | |
| 1571 | + for h in &shown { | |
| 1532 | 1572 | let Ok(day) = Month::try_from(h.date.month() as u8).and_then(|month| { |
| 1533 | 1573 | Date::from_calendar_date(h.date.year(), month, h.date.day() as u8) |
| 1534 | 1574 | }) else { |
@@ -1543,7 +1583,10 @@ fn render(f: &mut Frame, app: &mut Tui) {
| 1543 | 1583 | ); |
| 1544 | 1584 | } |
| 1545 | 1585 | |
| 1546 | − for u in as_upcoming.iter().filter(|d| d.date.do_remind) { | |
| 1586 | + for u in as_upcoming | |
| 1587 | + .iter() | |
| 1588 | + .filter(|d| d.date.do_remind && show_people) | |
| 1589 | + { | |
| 1547 | 1590 | let Ok(day) = Month::try_from(u.date.date.month() as u8).and_then(|month| { |
| 1548 | 1591 | Date::from_calendar_date(start.year(), month, u.date.date.day() as u8) |
| 1549 | 1592 | }) else { |
@@ -1561,12 +1604,13 @@ fn render(f: &mut Frame, app: &mut Tui) {
| 1561 | 1604 | // People's dates and holidays together, in date order |
| 1562 | 1605 | let mut upcoming = as_upcoming |
| 1563 | 1606 | .iter() |
| 1607 | + .filter(|_| show_people) | |
| 1564 | 1608 | .map(|u| (u.date.date, None, ListItem::from(u))) |
| 1565 | 1609 | .collect::<Vec<_>>(); |
| 1566 | 1610 | upcoming.extend( |
| 1567 | − app.holidays | |
| 1611 | + shown | |
| 1568 | 1612 | .iter() |
| 1569 | − .map(|h| (h.date, Some(h.clone()), holiday_item(h))), | |
| 1613 | + .map(|h| (h.date, Some((*h).clone()), holiday_item(h))), | |
| 1570 | 1614 | ); |
| 1571 | 1615 | upcoming.sort_by_key(|(d, _, _)| *d); |
| 1572 | 1616 | // Open on the first date from today on, with a couple of past ones above it |
@@ -1655,32 +1699,46 @@ fn render(f: &mut Frame, app: &mut Tui) {
| 1655 | 1699 | // .constraints([Constraint::Percentage(25), Constraint::Percentage(75)]) |
| 1656 | 1700 | // .split(chunks[1]); |
| 1657 | 1701 | |
| 1658 | − let msgw = vec![ | |
| 1702 | + let key = |k: &str| { | |
| 1659 | 1703 | Span::styled( |
| 1660 | − "Tab", | |
| 1704 | + k.to_string(), | |
| 1661 | 1705 | Style::default().add_modifier(Modifier::BOLD).fg(WHITE), |
| 1662 | − ), | |
| 1663 | − Span::raw(" to switch views. "), | |
| 1664 | − Span::styled("E", Style::default().add_modifier(Modifier::BOLD).fg(WHITE)), | |
| 1665 | − Span::raw(" to edit the enabled tiers. "), | |
| 1666 | − Span::styled("C", Style::default().add_modifier(Modifier::BOLD).fg(WHITE)), | |
| 1706 | + ) | |
| 1707 | + }; | |
| 1708 | + let msgw = vec![ | |
| 1709 | + key("Tab"), | |
| 1710 | + Span::raw(" views · "), | |
| 1711 | + key("c"), | |
| 1667 | 1712 | Span::raw(format!( |
| 1668 | − " to choose built-in holiday calendars ({} on). ", | |
| 1713 | + " calendars ({} on) · ", | |
| 1669 | 1714 | app.calendars_enabled.len() |
| 1670 | 1715 | )), |
| 1671 | − Span::styled( | |
| 1672 | − "↑/↓", | |
| 1673 | − Style::default().add_modifier(Modifier::BOLD).fg(WHITE), | |
| 1674 | − ), | |
| 1675 | − Span::raw(" to scroll upcoming, "), | |
| 1676 | − Span::styled( | |
| 1677 | − "PgUp/PgDn", | |
| 1678 | − Style::default().add_modifier(Modifier::BOLD).fg(WHITE), | |
| 1679 | − ), | |
| 1680 | − Span::raw(" for another year. "), | |
| 1681 | − Span::styled("i", Style::default().add_modifier(Modifier::BOLD).fg(WHITE)), | |
| 1682 | − Span::raw(" about a holiday. "), | |
| 1716 | + key("↑/↓"), | |
| 1717 | + Span::raw(" scroll · "), | |
| 1718 | + key("PgUp/PgDn"), | |
| 1719 | + Span::raw(" year · "), | |
| 1720 | + key("i"), | |
| 1721 | + Span::raw(" about · show:"), | |
| 1683 | 1722 | ]; |
| 1723 | + let mut msgw = msgw; | |
| 1724 | + // Like the ppl tab: a hidden kind is struck through | |
| 1725 | + for (key, what, label) in CAL_TOGGLES { | |
| 1726 | + let hidden = app.cal_hidden.contains(&what); | |
| 1727 | + let style = match hidden { | |
| 1728 | + true => Style::default() | |
| 1729 | + .fg(SLATE.c600) | |
| 1730 | + .add_modifier(Modifier::CROSSED_OUT), | |
| 1731 | + false => Style::default(), | |
| 1732 | + }; | |
| 1733 | + msgw.push(Span::raw(" ")); | |
| 1734 | + msgw.push(Span::styled( | |
| 1735 | + key.to_string(), | |
| 1736 | + style | |
| 1737 | + .add_modifier(Modifier::BOLD) | |
| 1738 | + .fg(if hidden { SLATE.c600 } else { WHITE }), | |
| 1739 | + )); | |
| 1740 | + msgw.push(Span::styled(format!(" {}", label), style)); | |
| 1741 | + } | |
| 1684 | 1742 | let textw = Text::from(Line::from(msgw)).style(Style::default()); |
| 1685 | 1743 | let welcome = Paragraph::new(textw); |
| 1686 | 1744 | f.render_widget(welcome, chunks[2]); |
Parents: 2c2327e