use bladvak::eframe::egui::{self};
use bladvak::utils::grid::Grid;
use bladvak::utils::is_native;
use bladvak::{AppError, BladvakApp, ErrorManager, File, eframe, utils::Documents};
use resvg::usvg;
use std::fmt::Debug;
use std::path::PathBuf;
use crate::document::Document;
use crate::settings::AppSettings;
use crate::string_viewer::StringViewerPanel;
use crate::svg_render::SvgViewerPanel;
use crate::tree_viewer::TreeViewerPanel;
use crate::{string_viewer::StringViewer, tree_viewer::TreeViewer};
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] pub struct GalagoApp {
pub(crate) documents: Documents<Document>,
pub(crate) settings: AppSettings,
pub(crate) tree_viewer: TreeViewer,
pub(crate) string_viewer: StringViewer,
pub(crate) grid: Grid,
#[serde(skip)]
pub(crate) usvg_options: usvg::Options<'static>,
}
impl Default for GalagoApp {
fn default() -> Self {
let mut usvg_options = usvg::Options::default();
usvg_options.fontdb_mut().load_system_fonts();
let mut documents = Documents::default();
documents.push(Self::default_document());
Self {
documents,
settings: AppSettings::default(),
tree_viewer: TreeViewer::default(),
string_viewer: StringViewer::default(),
grid: Grid::default(),
usvg_options,
}
}
}
impl Debug for GalagoApp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug_fmt = f.debug_struct("GalagoApp");
debug_fmt.finish()
}
}
impl GalagoApp {
pub(crate) const BASE_SVG: &str = include_str!("../assets/galago.svg");
pub(crate) fn default_document() -> Document {
Document {
svg: Self::BASE_SVG.to_string(),
saved_svg: Self::BASE_SVG.to_string(),
scene_rect: egui::Rect::NAN,
svg_is_valid: true,
filename: PathBuf::from("galago.svg"),
..Default::default()
}
}
pub(crate) fn handle_file_string(&mut self, filename: PathBuf, svg_str: String) {
let document = Document {
svg: svg_str.clone(),
saved_svg: svg_str,
filename,
..Default::default()
};
self.documents.push(document);
}
}
impl BladvakApp<'_> for GalagoApp {
fn panel_list(&self) -> Vec<Box<dyn bladvak::app::BladvakPanel<App = Self>>> {
vec![
Box::new(StringViewerPanel),
Box::new(TreeViewerPanel),
Box::new(SvgViewerPanel),
]
}
fn try_new_with_args(
saved_state: Self,
_cc: &eframe::CreationContext<'_>,
args: &[String],
_error_manager: &mut ErrorManager,
) -> Result<Self, AppError> {
let mut app = saved_state;
if is_native() && args.len() > 1 {
use std::fs;
for one_path in &args[1..] {
let absolute_path = fs::canonicalize(one_path)?;
match fs::read_to_string(&absolute_path) {
Ok(svg) => {
let document = Document {
saved_svg: svg.clone(),
svg,
filename: absolute_path,
..Default::default()
};
app.documents.push(document);
}
Err(e) => {
eprintln!("Failed to load svg '{}': {e}", absolute_path.display());
return Err((
format!("Failed to load svg '{}')", absolute_path.display()),
e,
)
.into());
}
}
}
Ok(app)
} else {
if !app.documents.is_some() {
app.documents.push(Self::default_document());
}
Ok(app)
}
}
fn top_panel(&mut self, ui: &mut egui::Ui, error_manager: &mut bladvak::ErrorManager) {
self.app_top_panel(ui, error_manager);
}
fn name() -> String {
env!("CARGO_PKG_NAME").to_string()
}
fn version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
fn repo_url() -> String {
"https://github.com/Its-Just-Nans/galago".to_string()
}
fn icon() -> &'static [u8] {
&include_bytes!("../assets/icon-256.png")[..]
}
fn central_panel(&mut self, ui: &mut egui::Ui, error_manager: &mut bladvak::ErrorManager) {
self.app_central_panel(ui, error_manager);
}
fn handle_file(&mut self, file: File) -> Result<(), AppError> {
let most_likely_font = file
.path
.extension()
.and_then(|e| e.to_str())
.is_some_and(|e| {
matches!(
e.to_ascii_lowercase().as_str(),
"ttf" | "otf" | "woff" | "woff2" | "eot" | "ttc"
)
});
if most_likely_font {
self.usvg_options.fontdb_mut().load_font_data(file.data);
for one_document in &mut self.documents {
one_document.svg_render.stale_render();
}
Ok(())
} else {
match String::from_utf8(file.data) {
Ok(svg_str) => {
self.handle_file_string(file.path, svg_str);
Ok(())
}
Err(e) => Err(e.into()),
}
}
}
fn is_side_panel(&self) -> bool {
self.documents.is_some()
}
fn side_panel(&mut self, ui: &mut egui::Ui, func_ui: impl FnOnce(&mut egui::Ui, &mut Self)) {
func_ui(ui, self);
}
fn is_open_button(&self) -> bool {
true
}
fn menu_file(&mut self, ui: &mut egui::Ui, error_manager: &mut bladvak::ErrorManager) {
if self.documents.is_some() && ui.button("Save").clicked() {
ui.close();
let Some(document) = self.documents.get_current_doc_mut() else {
error_manager.add_error("No document to save");
return;
};
let current_save_path = document.filename.clone();
let save_path = bladvak::utils::get_save_path(Some(¤t_save_path));
match save_path {
Ok(save_p) => {
if let Some(path_to_save) = save_p {
document.filename.clone_from(&path_to_save);
if let Err(err) = self.save_svg(&path_to_save) {
error_manager.add_error(err);
}
}
}
Err(e) => {
error_manager.add_error(e);
}
}
}
}
}