Menu
cosmic-mobile
publicLatest change d22956783f74f68f3d7eff1dd31f885aaf0fbe3e - Checkpoint BúiOS prototype and document clean-worktree workflow by AkurAI Build
use eframe::egui::*;
use std::{
path::Path,
time::{Duration, Instant},
};
pub fn save_note(path: &Path, text: &str) -> std::io::Result<()> {
if let Some(p) = path.parent() {
std::fs::create_dir_all(p)?;
}
let tmp = path.with_extension("tmp");
std::fs::write(&tmp, text)?;
std::fs::rename(tmp, path)
}
pub struct Calculator {
pub display: String,
value: f64,
op: String,
fresh: bool,
}
impl Default for Calculator {
fn default() -> Self {
Self {
display: "0".into(),
value: 0.,
op: String::new(),
fresh: true,
}
}
}
impl Calculator {
pub fn key(&mut self, k: &str) {
if k == "AC" {
*self = Self::default();
return;
}
if k.chars().all(|c| c.is_ascii_digit()) || k == "." {
if self.fresh {
self.display = "0".into();
self.fresh = false;
}
if k == "." {
if !self.display.contains('.') {
self.display.push('.');
}
} else if self.display.len() < 15 {
if self.display == "0" {
self.display.clear();
}
self.display.push_str(k);
}
return;
}
let Ok(n) = self.display.parse::<f64>() else {
return;
};
if k == "±" {
self.display = (-n).to_string();
return;
}
if k == "%" {
self.display = (n / 100.).to_string();
return;
}
if !self.op.is_empty() && !self.fresh {
let v = match self.op.as_str() {
"+" => self.value + n,
"−" => self.value - n,
"×" => self.value * n,
"÷" => self.value / n,
_ => n,
};
if !v.is_finite() {
self.display = "Error".into();
self.op.clear();
self.fresh = true;
return;
}
self.display = v.to_string();
}
self.value = self.display.parse().unwrap_or(0.);
self.op = if k == "=" { String::new() } else { k.into() };
self.fresh = true;
}
pub fn ui(&mut self, ui: &mut Ui) {
ui.horizontal(|ui| {
ui.label(RichText::new(&self.display).size(42.));
});
ui.add_space(24.);
let w = (ui.available_width() - 24.) / 4.;
for row in [
["AC", "±", "%", "÷"],
["7", "8", "9", "×"],
["4", "5", "6", "−"],
["1", "2", "3", "+"],
["0", ".", "=", ""],
] {
ui.horizontal(|ui| {
for k in row {
if k.is_empty() {
continue;
}
let orange = ["÷", "×", "−", "+", "="].contains(&k);
if ui
.add_sized(
[w, 56.],
Button::new(RichText::new(k).size(24.))
.fill(if orange {
Color32::from_rgb(160, 82, 0)
} else {
Color32::from_rgb(48, 52, 62)
})
.corner_radius(28),
)
.clicked()
{
self.key(k);
}
}
});
ui.add_space(8.);
}
}
}
#[derive(Default)]
pub struct Stopwatch {
elapsed: Duration,
started: Option<Instant>,
}
impl Stopwatch {
pub fn ui(&mut self, ui: &mut Ui) {
let elapsed = self.elapsed + self.started.map(|s| s.elapsed()).unwrap_or_default();
ui.label(
RichText::new(format!(
"{:02}:{:02}.{:01}",
elapsed.as_secs() / 60,
elapsed.as_secs() % 60,
elapsed.subsec_millis() / 100
))
.size(40.),
);
ui.add_space(24.);
ui.horizontal(|ui| {
if ui
.add_sized(
[120., 48.],
Button::new(if self.started.is_some() {
"Pause"
} else {
"Start"
}),
)
.clicked()
{
if let Some(s) = self.started.take() {
self.elapsed += s.elapsed();
} else {
self.started = Some(Instant::now());
}
}
if ui.add_sized([120., 48.], Button::new("Reset")).clicked() {
*self = Self::default();
}
});
if self.started.is_some() {
ui.ctx().request_repaint_after(Duration::from_millis(100));
}
}
}