#[derive(Debug, Clone)]
pub struct RichLogState {
pub entries: Vec<RichLogEntry>,
pub(crate) scroll_offset: usize,
pub auto_scroll: bool,
pub max_entries: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct RichLogEntry {
pub segments: Vec<(String, Style)>,
}
impl RichLogState {
pub const DEFAULT_MAX_ENTRIES: usize = 10_000;
pub fn new() -> Self {
Self {
max_entries: Some(Self::DEFAULT_MAX_ENTRIES),
..Self::new_unbounded()
}
}
pub fn new_unbounded() -> Self {
Self {
entries: Vec::new(),
scroll_offset: 0,
auto_scroll: true,
max_entries: None,
}
}
pub fn push(&mut self, text: impl Into<String>, style: Style) {
self.push_segments(vec![(text.into(), style)]);
}
pub fn push_plain(&mut self, text: impl Into<String>) {
self.push(text, Style::new());
}
pub fn push_segments(&mut self, segments: Vec<(String, Style)>) {
self.entries.push(RichLogEntry { segments });
if let Some(max_entries) = self.max_entries {
if self.entries.len() > max_entries {
let remove_count = self.entries.len() - max_entries;
self.entries.drain(0..remove_count);
self.scroll_offset = self.scroll_offset.saturating_sub(remove_count);
}
}
if self.auto_scroll {
self.scroll_offset = usize::MAX;
}
}
pub fn clear(&mut self) {
self.entries.clear();
self.scroll_offset = 0;
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
impl Default for RichLogState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct CalendarState {
pub year: i32,
pub month: u32,
pub selected_day: Option<u32>,
pub(crate) cursor_day: u32,
}
impl CalendarState {
pub fn new() -> Self {
let (year, month) = Self::current_year_month();
Self::from_ym(year, month)
}
pub fn from_ym(year: i32, month: u32) -> Self {
let month = month.clamp(1, 12);
Self {
year,
month,
selected_day: None,
cursor_day: 1,
}
}
pub fn selected_date(&self) -> Option<(i32, u32, u32)> {
self.selected_day.map(|day| (self.year, self.month, day))
}
pub fn prev_month(&mut self) {
if self.month == 1 {
self.month = 12;
self.year -= 1;
} else {
self.month -= 1;
}
self.clamp_days();
}
pub fn next_month(&mut self) {
if self.month == 12 {
self.month = 1;
self.year += 1;
} else {
self.month += 1;
}
self.clamp_days();
}
pub(crate) fn days_in_month(year: i32, month: u32) -> u32 {
match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 => {
if Self::is_leap_year(year) {
29
} else {
28
}
}
_ => 30,
}
}
pub(crate) fn first_weekday(year: i32, month: u32) -> u32 {
let month = month.clamp(1, 12);
let offsets = [0_i32, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];
let mut y = year;
if month < 3 {
y -= 1;
}
let sunday_based = (y + y / 4 - y / 100 + y / 400 + offsets[(month - 1) as usize] + 1) % 7;
((sunday_based + 6) % 7) as u32
}
fn clamp_days(&mut self) {
let max_day = Self::days_in_month(self.year, self.month);
self.cursor_day = self.cursor_day.clamp(1, max_day);
if let Some(day) = self.selected_day {
self.selected_day = Some(day.min(max_day));
}
}
fn is_leap_year(year: i32) -> bool {
(year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}
fn current_year_month() -> (i32, u32) {
let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) else {
return (1970, 1);
};
let days_since_epoch = (duration.as_secs() / 86_400) as i64;
let (year, month, _) = Self::civil_from_days(days_since_epoch);
(year, month)
}
fn civil_from_days(days_since_epoch: i64) -> (i32, u32, u32) {
let z = days_since_epoch + 719_468;
let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
let doe = z - era * 146_097;
let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
let mut year = (yoe as i32) + (era as i32) * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let day = (doy - (153 * mp + 2) / 5 + 1) as u32;
let month = (mp + if mp < 10 { 3 } else { -9 }) as u32;
if month <= 2 {
year += 1;
}
(year, month, day)
}
}
impl Default for CalendarState {
fn default() -> Self {
Self::new()
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ButtonVariant {
#[default]
Default,
Primary,
Danger,
Outline,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Trend {
Up,
Down,
}