mod command;
mod mode;
mod menu;
mod input;
mod help;
mod app;
mod dispatch;
mod render;
use mode::Mode;
use ticker_core::{
AnyDatasheet,
clear_eval_cache,
FormulaTree, CellKind,
};
pub(crate) use app::App;
pub(crate) const ROW_H: f32 = 22.0;
pub(crate) const TICK_COL_W: f32 = 52.0;
pub(crate) const PAGE_ROWS: usize = 20;
pub(crate) const COLOR_CURSOR_BG: egui::Color32 = egui::Color32::from_rgb(0, 120, 215);
pub(crate) const COLOR_CURSOR_FG: egui::Color32 = egui::Color32::WHITE;
pub(crate) const COLOR_FORMULA_CELL: egui::Color32 = egui::Color32::from_rgb(40, 180, 99);
pub(crate) const COLOR_PROPAGATED: egui::Color32 = egui::Color32::from_rgb(120, 120, 120);
pub(crate) const COLOR_ERROR: egui::Color32 = egui::Color32::from_rgb(220, 53, 69);
pub(crate) const COLOR_HEADER_BG: egui::Color32 = egui::Color32::from_rgb(40, 40, 40);
pub(crate) const COLOR_HEADER_FG: egui::Color32 = egui::Color32::from_rgb(180, 200, 220);
pub(crate) const COLOR_TICK_FG: egui::Color32 = egui::Color32::from_rgb(120, 120, 140);
pub(crate) const COLOR_SEPARATOR: egui::Color32 = egui::Color32::from_rgb(60, 60, 60);
pub(crate) struct ActionCategory {
pub(crate) name: &'static str,
pub(crate) key: char,
pub(crate) cmds: &'static [&'static str],
}
pub(crate) const ACTION_CATEGORIES: &[ActionCategory] = &[
ActionCategory { name: "Project", key: 'P', cmds: &["rp", "w", "o", "q", "h"] },
ActionCategory { name: "Sheets", key: 'S', cmds: &["as", "rs", "ds", "2s", "filter", "hideds", "showds", "listds"] },
ActionCategory { name: "Columns", key: 'C', cmds: &["ac", "rc", "dc", "cv", "ml", "mr", "hc", "sh", "g", "f", "sf"] },
ActionCategory { name: "Ticks", key: 'T', cmds: &["it", "dt", "ct", "cat"] },
ActionCategory { name: "Properties", key: 'R', cmds: &["sp", "dp"] },
];
pub(crate) fn action_category_for_key(key: char) -> Option<&'static ActionCategory> {
ACTION_CATEGORIES.iter().find(|c| c.key == key.to_ascii_uppercase())
}
pub(crate) fn formula_tree_cursor_text(tree: &FormulaTree) -> String {
match tree.cursor.kind {
CellKind::Wrap => String::new(),
CellKind::Content => tree.node_at(&tree.cursor.path).content_text().to_owned(),
}
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
ctx.set_pixels_per_point(self.base_dpi * self.zoom);
if !self.menu_initialized {
#[cfg(target_os = "macos")]
self.menu_bar.init_for_nsapp();
self.menu_initialized = true;
}
while let Ok(event) = muda::MenuEvent::receiver().try_recv() {
if let Some(cmd) = self.menu_ids.command_for_event(&event) {
self.execute(cmd);
}
}
if let Some(cmd) =
input::process(ctx, &self.mode, &self.edit_buffer, self.prop_focused)
{
self.execute(cmd);
}
if let Some(ds) = self.active_ds() {
clear_eval_cache(ds);
} else if let Some(fd) = self.active_filtered_ds() {
let source_id = fd.source.clone();
if let Some(AnyDatasheet::Normal(src_ds)) = self.project.datasheets.get(&source_id) {
clear_eval_cache(src_ds);
}
}
self.render_footer(ctx);
self.render_hints_bar(ctx);
self.render_summary_bar(ctx);
self.render_sheet_tabs(ctx);
let col_ids = self.data_col_ids();
self.render_col_headers(ctx, &col_ids.clone());
self.render_aggr_row(ctx, &col_ids.clone());
self.render_fmt_row(ctx, &col_ids.clone());
self.render_property_panel(ctx);
egui::CentralPanel::default().show(ctx, |ui| {
if self.mode == Mode::Help {
self.render_help(ui);
} else {
let col_ids_clone = col_ids.clone();
if let Some(cmd) = self.render_grid(ui, &col_ids_clone) {
self.execute(cmd);
}
}
});
let viewport_rows = 30;
let viewport_cols = 8;
self.scroll_into_view(viewport_rows, viewport_cols);
ctx.request_repaint();
}
}
fn main() -> eframe::Result<()> {
let (menu_bar, menu_ids) = menu::build_menu();
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([1400.0, 900.0])
.with_min_inner_size([600.0, 400.0])
.with_title("Ticker"),
..Default::default()
};
eframe::run_native(
"Ticker",
native_options,
Box::new(move |cc| Ok(Box::new(App::new(cc, menu_ids, menu_bar)))),
)
}