use eframe::egui; use json::JsonValue; use std::io::{self, BufRead}; use std::sync::mpsc::{sync_channel, Receiver}; use std::thread; fn main() -> Result<(), eframe::Error> { eframe::run_native( "Grazer", eframe::NativeOptions { initial_window_size: Some(egui::vec2(480.0, 360.0)), ..Default::default() }, Box::new(|_cc| Box::<MyApp>::default()), ) } struct MyApp { address: String, recv: Option<Receiver<JsonValue>>, last: JsonValue, } impl Default for MyApp { fn default() -> Self { Self { address: "about:grazer".to_owned(), last: json::parse("[[\"h1\",[],[\"Where would you like to graze today?\"]], [\"p\",[],[\"\"]], [\"p\",[],[\"🌱👩🌿🌾🌿🌷🌸🌱💮🍊🌾🌸🌱🍅🐌🍆🌲🌹🍃🌾🐌🍇🌱🍓🌲💚🌼\"]]]").unwrap(), recv: None, } } } impl MyApp { fn maybe_start(&mut self, ctx: &egui::Context) { if self.recv.is_none() { let ctx_clone = ctx.clone(); let (sync_sender, receiver) = sync_channel(2); self.recv = Some(receiver); thread::spawn(move || { for l in io::stdin().lock().lines().map(|l| l.unwrap()) { //eprintln!("Rust got {}", l); sync_sender .send(json::parse(&l).expect("Invalid JSON")) .expect("Failed to send"); ctx_clone.request_repaint(); } std::process::exit(0); }); } } fn walk_dom(&mut self, dom: &json::JsonValue, ui: &mut egui::Ui) { use json::JsonValue::*; match dom { Short(s) => { ui.label(s.as_str().replace("\n", " ")); } String(s) => { ui.label(s.as_str().replace("\n", " ")); } Array(vec) => { if vec.len() == 3 && vec[0].is_string() { match vec[0].as_str().unwrap() { "head" | "script" | "style" | "comment" => {} "h1" => { ui.heading(vec[2][0].as_str().expect("text in h1")); } "a" => { if ui.link(vec[2][0].as_str().expect("text in a")).clicked() { vec[1]["href"].as_str().and_then(|href| { println!("navigate {}", href); self.address = href.to_owned(); Some(()) }); } } "p" => { //ui.style_mut().spacing.interact_size.y = 0.0; ui.horizontal_wrapped(|ui| { ui.spacing_mut().item_spacing.x = 0.0; for val in vec[2].members() { self.walk_dom(val, ui) } }); } s => { for val in vec[2].members() { self.walk_dom(val, ui) } } } } else { for val in vec { self.walk_dom(val, ui) } } } _ => panic!("Unexpected {:?}", dom), }; } } impl eframe::App for MyApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { self.maybe_start(ctx); loop { match self.recv.as_mut().unwrap().try_recv() { Ok(line) => self.last = line, Err(_) => break, }; } egui::TopBottomPanel::top("my_panel") //.frame(egui::containers::Frame::none() // .inner_margin(egui::style::Margin::same(10.0))) .show(ctx, |ui| { ui.horizontal(|ui| { let address_label = ui.label("🌍"); let response = ui .text_edit_singleline(&mut self.address) .labelled_by(address_label.id); if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { println!("navigate {}", self.address); } if ui.button("Graze").clicked() { println!("navigate {}", self.address); } }); }); egui::CentralPanel::default().show(ctx, |ui| { egui::ScrollArea::vertical().show(ui, |ui| { self.walk_dom(&self.last.clone(), ui); }); }); } }