From 97d6f5e86d7b6677afb53c5701a27c991f337ba1 Mon Sep 17 00:00:00 2001 From: Spydr06 Date: Wed, 2 Nov 2022 14:02:38 +0100 Subject: [PATCH] Load & Save working --- Cargo.toml | 2 + src/application/data.rs | 75 ++++++++++++++++--- src/application/template.rs | 16 +++- src/main.rs | 22 ++++-- src/modules/builtin.rs | 8 +- src/modules/mod.rs | 15 ++++ src/simulator/block.rs | 27 ++++--- src/ui/dialogs.rs | 14 ++-- src/ui/mod.rs | 2 +- src/ui/module_list.rs | 2 +- test.json | 141 +----------------------------------- 11 files changed, 142 insertions(+), 182 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd3af81..0077944 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,5 @@ version = "0.4.8" glib = "0.15.12" serde_json = "1.0" serde = {version = "1.0.147", features = ["derive"]} +log = "0.4.0" +env_logger = "0.9.0" diff --git a/src/application/data.rs b/src/application/data.rs index 419787c..3a713e8 100644 --- a/src/application/data.rs +++ b/src/application/data.rs @@ -1,10 +1,11 @@ use std::{ collections::HashMap, - io::BufReader, - fs::File, - cmp, path::Path + io::{BufReader, Write}, + fs::{File, OpenOptions}, + sync::atomic::{AtomicU32, Ordering}, + cmp, }; - +use gtk::gio::{self, prelude::FileExt}; use crate::{ modules::{ Module, @@ -55,9 +56,15 @@ impl Selection { pub struct ApplicationData { modules: HashMap, plots: Vec, + id_counter: AtomicU32, + + #[serde(skip)] + file: Option, + + #[serde(skip)] current_plot: usize, - file: Option, + #[serde(skip)] selection: Selection } @@ -76,6 +83,7 @@ impl ApplicationData { modules: HashMap::new(), plots: vec![Plot::new()], current_plot: 0usize, + id_counter: AtomicU32::new(0u32), file: None, selection: Selection::None }; @@ -84,10 +92,8 @@ impl ApplicationData { data } - pub fn build

(path: P) -> Result - where P: AsRef - { - let f = File::open(path); + pub fn build(file: gio::File) -> Result { + let f = File::open(file.path().unwrap()); if let Err(err) = f { return Err(err.to_string()); } @@ -96,11 +102,60 @@ impl ApplicationData { let result: serde_json::Result = serde_json::from_reader(reader); match result { - Ok(data) => Ok(data), + Ok(mut data) => { + info!("Opened file `{}`", file.path().unwrap().to_str().unwrap()); + data.set_file(Some(file)); + Ok(data) + }, Err(err) => Err(err.to_string()), } } + pub fn save(&self) -> Result<(), String> { + if (&self.file).is_none() { + return Err("save_as is not implemented".to_string()); + } + + let file = self.file.as_ref().unwrap(); + + info!("Saving to `{}` ...", file.path().unwrap().to_str().unwrap()); + let res = OpenOptions::new().write(true).truncate(true).open(file.path().unwrap()); + if let Err(err) = res { + return Err(err.to_string()); + } + + let mut f = res.unwrap(); + let result = serde_json::to_string(self); + match result { + Ok(serialized) => { + let res = f.write(serialized.as_bytes()); + match res { + Ok(bytes_written) => { + info!("Wrote {} bytes to `{}` successfully", bytes_written, file.path().unwrap().to_str().unwrap()); + Ok(()) + } + Err(err) => { + Err(err.to_string()) + } + } + }, + Err(err) => Err(err.to_string()) + } + } + + pub fn new_id(&self) -> u32 { + self.id_counter.fetch_add(1u32, Ordering::SeqCst) + } + + pub fn file(&self) -> &Option { + &self.file + } + + pub fn set_file(&mut self, file: Option) -> &mut Self { + self.file = file; + self + } + pub fn add_module(&mut self, module: Module) -> &mut Self { self.modules.insert(module.name().clone(), module); self diff --git a/src/application/template.rs b/src/application/template.rs index 09c0fbf..81c6292 100644 --- a/src/application/template.rs +++ b/src/application/template.rs @@ -61,7 +61,7 @@ impl ApplicationImpl for ApplicationTemplate { crate::die("File path is None"); } - let data = ApplicationData::build(file.path().unwrap()); + let data = ApplicationData::build(file.to_owned()); if let Err(err) = data { crate::die(err.as_str()); } @@ -69,6 +69,20 @@ impl ApplicationImpl for ApplicationTemplate { crate::APPLICATION_DATA.with(|d| d.replace(data.unwrap())); self.create_window(application); } + + fn shutdown(&self, _application: &Self::Type) { + let res = crate::APPLICATION_DATA.with(|d| { + let data = d.borrow(); + match data.file() { + Some(_) => data.save(), + None => Err("save_as is not implemented()".to_string()), + } + }); + + if let Err(err) = res { + crate::die(err.as_str()) + } + } } impl GtkApplicationImpl for ApplicationTemplate {} impl AdwApplicationImpl for ApplicationTemplate {} diff --git a/src/main.rs b/src/main.rs index e4b4b63..007c17e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,9 @@ mod modules; mod renderer; mod simulator; +#[macro_use] +extern crate log; + use std::cell::RefCell; use adw::prelude::ApplicationExtManual; use application::{ @@ -25,10 +28,10 @@ fn init_new() { let not_mod = data.get_module(&"Not".to_string()).unwrap(); let xor_mod = data.get_module(&"Xor".to_string()).unwrap(); - let mut block1 = Block::new(&and_mod, (10, 10)); - let mut block2 = Block::new(&or_mod, (110, 10)); - let block3 = Block::new(¬_mod, (210, 10)); - let block4 = Block::new(&xor_mod, (310, 10)); + let mut block1 = Block::new(&and_mod, (10, 10), data.new_id()); + let mut block2 = Block::new(&or_mod, (110, 10), data.new_id()); + let block3 = Block::new(¬_mod, (210, 10), data.new_id()); + let block4 = Block::new(&xor_mod, (310, 10), data.new_id()); block1.connect_to(0u8, Linkage {block_id: 1u32, port: 1u8}); block2.connect_to(0u8, Linkage {block_id: 2u32, port: 0u8}); @@ -47,12 +50,17 @@ fn init_new() { }); } -fn main() -> std::io::Result<()> { +fn main() { + env_logger::init(); + + info!("Starting up LogicRs..."); + //init_new(); + let application = Application::new(); std::process::exit(application.run()); } pub fn die<'a>(reason: &'a str) -> ! { - eprintln!("Fatal error: `{reason}`"); - std::process::exit(1) + error!("{reason}"); + panic!() } \ No newline at end of file diff --git a/src/modules/builtin.rs b/src/modules/builtin.rs index eb5779b..fa789ac 100644 --- a/src/modules/builtin.rs +++ b/src/modules/builtin.rs @@ -1,8 +1,8 @@ use super::Module; pub fn register(data: &mut crate::application::data::ApplicationData) { - data.add_module(Module::new("And".to_string(), 2, 1)); - data.add_module(Module::new("Or".to_string(), 2, 1)); - data.add_module(Module::new("Not".to_string(), 1, 1)); - data.add_module(Module::new("Xor".to_string(), 2, 1)); + data.add_module(Module::new_builtin("And".to_string(), 2, 1)); + data.add_module(Module::new_builtin("Or".to_string(), 2, 1)); + data.add_module(Module::new_builtin("Not".to_string(), 1, 1)); + data.add_module(Module::new_builtin("Xor".to_string(), 2, 1)); } \ No newline at end of file diff --git a/src/modules/mod.rs b/src/modules/mod.rs index ddf9c01..76460c7 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -5,6 +5,7 @@ use serde::{Serialize, Deserialize}; #[derive(Default, Debug, Serialize, Deserialize, Clone)] pub struct Module { name: String, + builtin: bool, num_inputs: u8, num_outputs: u8 } @@ -13,11 +14,25 @@ impl Module { pub fn new(name: String, num_inputs: u8, num_outputs: u8) -> Self { Self { name, + builtin: false, num_inputs, num_outputs } } + pub fn new_builtin(name: String, num_inputs: u8, num_outputs: u8) -> Self { + Self { + name, + builtin: true, + num_inputs, + num_outputs + } + } + + pub fn builtin(&self) -> bool { + self.builtin + } + pub fn name(&self) -> &String { &self.name } diff --git a/src/simulator/block.rs b/src/simulator/block.rs index 25a9bff..375dec2 100644 --- a/src/simulator/block.rs +++ b/src/simulator/block.rs @@ -1,8 +1,4 @@ -use std::{ - sync::atomic::{AtomicU32, Ordering}, - f64, - cmp -}; +use std::{f64, cmp}; use crate::{ modules::Module, @@ -23,20 +19,22 @@ pub enum Connector { #[derive(Debug, Serialize, Deserialize)] pub struct Block { id: u32, + name: String, + position: (i32, i32), start_pos: (i32, i32), // starting position of drag movements size: (i32, i32), + + #[serde(skip)] highlighted: bool, + num_inputs: u8, num_outputs: u8, - name: String, connections: Vec> } impl Block { - pub fn new(module: &&Module, position: (i32, i32)) -> Self { - static ID: AtomicU32 = AtomicU32::new(0u32); - + pub fn new(module: &&Module, position: (i32, i32), id: u32) -> Self { let num_inputs = module.get_num_inputs(); let num_outputs = module.get_num_outputs(); let mut connections = Vec::with_capacity(num_outputs as usize); @@ -44,15 +42,20 @@ impl Block { connections.push(None); } + let name = module.name().clone(); + Self { - id: ID.fetch_add(1u32, Ordering::SeqCst), + id, position, start_pos: (0, 0), - size: (75, cmp::max(num_inputs, num_outputs) as i32 * 25 + 50), + size: ( + cmp::max(75, (name.len() * 10) as i32), + cmp::max(num_inputs, num_outputs) as i32 * 25 + 50 + ), highlighted: false, num_inputs, num_outputs, - name: module.name().clone(), + name, connections, } } diff --git a/src/ui/dialogs.rs b/src/ui/dialogs.rs index 0c4a529..35f15f9 100644 --- a/src/ui/dialogs.rs +++ b/src/ui/dialogs.rs @@ -9,7 +9,7 @@ use gtk::{ use std::{future::Future, rc::Rc}; use crate::modules::Module; -fn create_new_module(name: String, num_inputs: i32, num_outputs: i32) -> Result<(), String> { +fn create_new_module(name: String, num_inputs: u8, num_outputs: u8) -> Result<(), String> { if name.is_empty() { return Err("Invalid name".to_string()); } @@ -19,12 +19,14 @@ fn create_new_module(name: String, num_inputs: i32, num_outputs: i32) -> Result< exists = data.borrow().module_exists(&name); }); if exists { - return Err(format!("Module with name \"{}\" already exists", name)); + let err = format!("Module with name \"{}\" already exists", name); + warn!("{err}"); + return Err(err); } - println!("Create new Module \"{}\"\nwith: {} inputs\n {} outputs", name, num_inputs, num_outputs); + info!("Create new Module \"{}\"\nwith: {} inputs\n {} outputs", name, num_inputs, num_outputs); crate::APPLICATION_DATA.with(|data| { - data.borrow_mut().add_module(Module::new(name, num_inputs as u8, num_outputs as u8)); + data.borrow_mut().add_module(Module::new(name, num_inputs, num_outputs)); }); Ok(()) @@ -108,7 +110,7 @@ pub async fn new_module>(window: Rc) { let num_outputs = OUTPUTS.iter().position(|&elem| elem == output_chooser.active_id().unwrap()).unwrap_or_default() + 1; // generate new module - if let Err(err) = create_new_module(name_input.buffer().text().trim().to_string(), num_inputs as i32, num_outputs as i32) { + if let Err(err) = create_new_module(name_input.buffer().text().trim().to_string(), num_inputs as u8, num_outputs as u8) { gtk::glib::MainContext::default().spawn_local(invalid_module(window, err)); } } @@ -116,7 +118,7 @@ pub async fn new_module>(window: Rc) { pub fn new(trigger: &Button, window_size: (i32, i32), on_trigger: fn(Rc) -> F) where - F: Future + 'static, + F: Future + 'static { let dialog_window = Rc::new( gtk::ApplicationWindow::builder() diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 56f72aa..39d21b9 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,5 +1,5 @@ mod circuit_panel; mod circuit_view; -mod dialogs; +pub mod dialogs; pub mod main_window; mod module_list; diff --git a/src/ui/module_list.rs b/src/ui/module_list.rs index 9994f57..9b755b2 100644 --- a/src/ui/module_list.rs +++ b/src/ui/module_list.rs @@ -104,7 +104,7 @@ fn new_list_item(module: &Module) -> ListBoxRow { crate::APPLICATION_DATA.with(|data| { let mut data = data.borrow_mut(); let module = data.get_module(&name).unwrap(); - let block = Block::new(&module, (0, 0)); + let block = Block::new(&module, (0, 0), data.new_id()); data.current_plot_mut().add_block(block); }); }); diff --git a/test.json b/test.json index dc62665..9307422 100644 --- a/test.json +++ b/test.json @@ -1,140 +1 @@ -{ - "modules": { - "Or": { - "name": "Or", - "num_inputs": 2, - "num_outputs": 1 - }, - "Not": { - "name": "Not", - "num_inputs": 1, - "num_outputs": 1 - }, - "Xor": { - "name": "Xor", - "num_inputs": 2, - "num_outputs": 1 - }, - "And": { - "name": "And", - "num_inputs": 2, - "num_outputs": 1 - } - }, - "plots": [ - { - "blocks": { - "0": { - "id": 0, - "position": [ - 10, - 10 - ], - "start_pos": [ - 0, - 0 - ], - "size": [ - 75, - 100 - ], - "highlighted": false, - "num_inputs": 2, - "num_outputs": 1, - "name": "And", - "connections": [ - { - "from": { - "block_id": 0, - "port": 0 - }, - "to": { - "block_id": 1, - "port": 1 - }, - "active": false - } - ] - }, - "1": { - "id": 1, - "position": [ - 110, - 10 - ], - "start_pos": [ - 0, - 0 - ], - "size": [ - 75, - 100 - ], - "highlighted": false, - "num_inputs": 2, - "num_outputs": 1, - "name": "Or", - "connections": [ - { - "from": { - "block_id": 1, - "port": 0 - }, - "to": { - "block_id": 2, - "port": 0 - }, - "active": false - } - ] - }, - "3": { - "id": 3, - "position": [ - 310, - 10 - ], - "start_pos": [ - 0, - 0 - ], - "size": [ - 75, - 100 - ], - "highlighted": false, - "num_inputs": 2, - "num_outputs": 1, - "name": "Xor", - "connections": [ - null - ] - }, - "2": { - "id": 2, - "position": [ - 210, - 10 - ], - "start_pos": [ - 0, - 0 - ], - "size": [ - 75, - 75 - ], - "highlighted": false, - "num_inputs": 1, - "num_outputs": 1, - "name": "Not", - "connections": [ - null - ] - } - } - } - ], - "current_plot": 0, - "selection": "None" -} \ No newline at end of file +{"modules":{"And":{"name":"And","builtin":true,"num_inputs":2,"num_outputs":1},"New Module":{"name":"New Module","builtin":false,"num_inputs":2,"num_outputs":1},"Xor":{"name":"Xor","builtin":true,"num_inputs":2,"num_outputs":1},"Not":{"name":"Not","builtin":true,"num_inputs":1,"num_outputs":1},"Or":{"name":"Or","builtin":true,"num_inputs":2,"num_outputs":1}},"plots":[{"blocks":{"6":{"id":6,"name":"New Module","position":[291,230],"start_pos":[269,311],"size":[100,100],"num_inputs":2,"num_outputs":1,"connections":[null]},"3":{"id":3,"name":"Xor","position":[31,33],"start_pos":[15,58],"size":[75,100],"num_inputs":2,"num_outputs":1,"connections":[{"from":{"block_id":3,"port":0},"to":{"block_id":1,"port":0},"active":false}]},"4":{"id":4,"name":"And","position":[35,206],"start_pos":[35,206],"size":[75,100],"num_inputs":2,"num_outputs":1,"connections":[{"from":{"block_id":4,"port":0},"to":{"block_id":1,"port":1},"active":false}]},"1":{"id":1,"name":"Or","position":[159,131],"start_pos":[159,131],"size":[75,100],"num_inputs":2,"num_outputs":1,"connections":[{"from":{"block_id":1,"port":0},"to":{"block_id":2,"port":0},"active":false}]},"0":{"id":0,"name":"Or","position":[461,41],"start_pos":[461,41],"size":[75,100],"num_inputs":2,"num_outputs":1,"connections":[null]},"2":{"id":2,"name":"Not","position":[292,143],"start_pos":[292,143],"size":[75,75],"num_inputs":1,"num_outputs":1,"connections":[null]}}}],"id_counter":7} \ No newline at end of file