AkurAI Build
Menu

cosmic-mobile

public

Latest change dc9bba1298897771c885931ff76e1a0995d3652b - Fix iOS control details: delete glyph, switches, pill zero, hidden scrollbar, truncated rows by AkurAI Build

use crate::ios;
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.with_layout(Layout::right_to_left(Align::Min), |ui| {
            ui.label(RichText::new(&self.display).size(72.));
        });
        ui.add_space(16.);
        let gap = 12.;
        let size = ((ui.available_width() - 3. * gap) / 4.).clamp(56., 84.);
        for row in [
            ["AC", "±", "%", "÷"],
            ["7", "8", "9", "×"],
            ["4", "5", "6", "−"],
            ["1", "2", "3", "+"],
            ["0", ".", "=", ""],
        ] {
            ui.horizontal(|ui| {
                ui.spacing_mut().item_spacing.x = gap;
                for k in row {
                    if k.is_empty() {
                        continue;
                    }
                    if k == "0" {
                        if ios::pill(ui, "0", size * 2. + gap, size, ios::KEY).clicked() {
                            self.key(k);
                        }
                        continue;
                    }
                    let (fill, text) = if ["÷", "×", "−", "+", "="].contains(&k) {
                        (ios::ORANGE, Color32::WHITE)
                    } else if ["AC", "±", "%"].contains(&k) {
                        (ios::KEY_LIGHT, Color32::BLACK)
                    } else {
                        (ios::KEY, Color32::WHITE)
                    };
                    if ios::key(ui, k, size, fill, text).clicked() {
                        self.key(k);
                    }
                }
            });
            ui.add_space(gap);
        }
    }
}
#[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.vertical_centered(|ui| {
            ui.label(
                RichText::new(format!(
                    "{:02}:{:02}.{:01}",
                    elapsed.as_secs() / 60,
                    elapsed.as_secs() % 60,
                    elapsed.subsec_millis() / 100
                ))
                .size(56.),
            );
        });
        ui.add_space(24.);
        ui.horizontal(|ui| {
            ui.spacing_mut().item_spacing.x = ui.available_width() - 2. * 84.;
            if ios::key(ui, "Reset", 84., ios::KEY, Color32::WHITE).clicked() {
                *self = Self::default();
            }
            let (label, fill) = if self.started.is_some() {
                ("Stop", ios::RED)
            } else {
                ("Start", ios::GREEN)
            };
            if ios::key(ui, label, 84., fill, Color32::WHITE).clicked() {
                if let Some(s) = self.started.take() {
                    self.elapsed += s.elapsed();
                } else {
                    self.started = Some(Instant::now());
                }
            }
        });
        if self.started.is_some() {
            ui.ctx().request_repaint_after(Duration::from_millis(100));
        }
    }
}