use std::{ io::{self, Read}, sync::mpsc, time::Duration, }; use iced::{ executor, time, widget::{button, column, row, text}, Application, Command, Element, Renderer, Settings, Subscription, Theme, }; use mers::{ libs::inlib::{MyLib, MyLibTask}, script::{ val_data::{VData, VDataEnum}, val_type::{VSingleType, VType}, }, }; /* Path: Vec */ fn single_gui_element() -> VType { VType { types: vec![ VSingleType::EnumVariantS("Row".to_string(), VSingleType::Tuple(vec![]).to()), VSingleType::EnumVariantS("Column".to_string(), VSingleType::Tuple(vec![]).to()), VSingleType::EnumVariantS("Text".to_string(), VSingleType::String.to()), VSingleType::EnumVariantS( "Button".to_string(), VType { types: vec![VSingleType::Tuple(vec![]), VSingleType::String], }, ), ], } } fn single_gui_update() -> VType { VType { types: vec![VSingleType::EnumVariantS( "ButtonPressed".to_string(), VSingleType::List(VSingleType::Int.to()).to(), )], } } fn main() { let (sender, recv) = mpsc::channel(); let (sender2, recv2) = mpsc::channel(); std::thread::spawn(move || { let recv = recv2; let (mut my_lib, mut run) = MyLib::new( "GUI-Iced".to_string(), (0, 0), "A basic GUI library for mers.".to_string(), vec![ ( "gui_init".to_string(), vec![], VSingleType::List(VSingleType::Int.to()).to(), ), ( "gui_updates".to_string(), vec![], VSingleType::List(single_gui_update()).to(), ), ( "set_title".to_string(), vec![VSingleType::String.to()], VSingleType::Tuple(vec![]).to(), ), ( "gui_add".to_string(), vec![ VSingleType::List(VSingleType::Int.to()).to(), single_gui_element(), ], VSingleType::List(VSingleType::Int.to()).to(), ), ( "gui_remove".to_string(), vec![VSingleType::List(VSingleType::Int.to()).to()], VSingleType::Tuple(vec![]).to(), ), ], ); let mut stdin = std::io::stdin().lock(); let mut stdout = std::io::stdout().lock(); let mut layout = Layout::Row(vec![]); loop { run = match my_lib.run(run, &mut stdin, &mut stdout) { MyLibTask::None(v) | MyLibTask::FinishedInit(v) => v, MyLibTask::RunFunction(mut f) => { let return_value = match f.function { 0 => VDataEnum::List(VSingleType::Int.to(), vec![]).to(), 1 => { let mut v = vec![]; while let Ok(recv) = recv.try_recv() { match recv { MessageAdv::ButtonPressed(path) => v.push( VDataEnum::EnumVariant( my_lib.get_enum("ButtonPressed").unwrap(), Box::new( VDataEnum::List(VSingleType::Int.to(), path).to(), ), ) .to(), ), } } VDataEnum::List(single_gui_update(), v).to() } 2 => { // set_title if let VDataEnum::String(new_title) = f.args.remove(0).data { sender.send(Task::SetTitle(new_title)).unwrap(); VDataEnum::Tuple(vec![]).to() } else { unreachable!() } } 3 => { // gui_add if let (layout_data, VDataEnum::List(_, path)) = (f.args.remove(1).data, f.args.remove(0).data) { let path: Vec = path .into_iter() .map(|v| { if let VDataEnum::Int(v) = v.data { v as _ } else { unreachable!() } }) .collect(); let lo = layout_from_vdata(&my_lib, layout_data); let layout_inner = layout.get_mut(&path, 0); let new_path: Vec<_> = path .iter() .map(|v| VDataEnum::Int(*v as _).to()) .chain( [VDataEnum::Int(layout_inner.len() as _).to()].into_iter(), ) .collect(); layout_inner.add(lo.clone()); sender.send(Task::LAdd(path, lo)).unwrap(); VDataEnum::List(VSingleType::Int.to(), new_path).to() } else { unreachable!() } } 4 => { // gui_remove if let VDataEnum::List(_, path) = f.args.remove(0).data { let mut path: Vec = path .into_iter() .map(|v| { if let VDataEnum::Int(v) = v.data { v as _ } else { unreachable!() } }) .collect(); if let Some(remove_index) = path.pop() { let layout_inner = layout.get_mut(&path, 0); layout_inner.remove(remove_index); path.push(remove_index); sender.send(Task::LRemove(path)).unwrap(); } VDataEnum::Tuple(vec![]).to() } else { unreachable!() } } _ => unreachable!(), }; f.done(&mut stdout, return_value) } } } }); App::run(Settings::with_flags((recv, sender2))).unwrap(); } fn layout_from_vdata(my_lib: &MyLib, d: VDataEnum) -> Layout { let row = my_lib.get_enum("Row").unwrap(); let col = my_lib.get_enum("Column").unwrap(); let text = my_lib.get_enum("Text").unwrap(); let button = my_lib.get_enum("Button").unwrap(); if let VDataEnum::EnumVariant(variant, inner_data) = d { if variant == row { Layout::Row(vec![]) } else if variant == col { Layout::Column(vec![]) } else if variant == text { Layout::Text(if let VDataEnum::String(s) = inner_data.data { s } else { String::new() }) } else if variant == button { Layout::Button(Box::new(Layout::Text( if let VDataEnum::String(s) = inner_data.data { s } else { String::new() }, ))) } else { unreachable!() } } else { unreachable!() } } enum Task { SetTitle(String), LAdd(Vec, Layout), LSet(Vec, Layout), LRemove(Vec), } struct App { title: String, recv: mpsc::Receiver, sender: mpsc::Sender, buttons: Vec>, layout: Layout, } #[derive(Debug, Clone, Copy)] enum Message { Tick, ButtonPressed(usize), } enum MessageAdv { ButtonPressed(Vec), } impl Application for App { type Executor = executor::Default; type Message = Message; type Theme = Theme; type Flags = (mpsc::Receiver, mpsc::Sender); fn new(flags: Self::Flags) -> (Self, Command) { ( Self { title: format!("mers gui (using iced)..."), recv: flags.0, sender: flags.1, buttons: vec![], layout: Layout::Column(vec![]), }, Command::none(), ) } fn title(&self) -> String { format!("{}", self.title) } fn update(&mut self, message: Self::Message) -> Command { let mut commands = vec![]; match message { Message::Tick => { let mut changed_layout = false; while let Ok(task) = self.recv.try_recv() { match task { Task::SetTitle(t) => { self.title = t; } Task::LAdd(path, add) => { changed_layout = true; self.layout.get_mut(&path, 0).add(add); } Task::LSet(path, add) => { changed_layout = true; *self.layout.get_mut(&path, 0) = add; } Task::LRemove(mut path) => { if let Some(last) = path.pop() { changed_layout = true; self.layout.get_mut(&path, 0).remove(last); } } } } if changed_layout { self.calc_layout_stats(); } } Message::ButtonPressed(bid) => self .sender .send(MessageAdv::ButtonPressed( self.buttons[bid] .iter() .map(|v| VDataEnum::Int(*v as _).to()) .collect(), )) .unwrap(), } Command::batch(commands) } fn subscription(&self) -> Subscription { time::every(Duration::from_millis(10)).map(|_| Message::Tick) } fn view(&self) -> Element<'_, Self::Message, Renderer> { self.viewl(&mut vec![], &self.layout, &mut 0) } } impl App { fn viewl( &self, p: &mut Vec, l: &Layout, current_button: &mut usize, ) -> Element<'_, ::Message, Renderer<::Theme>> { match l { Layout::Row(v) => row(v .iter() .enumerate() .map(|(i, v)| { p.push(i); let o = self.viewl(p, v, current_button); p.pop(); o }) .collect()) .into(), Layout::Column(v) => column( v.iter() .enumerate() .map(|(i, v)| { p.push(i); let o = self.viewl(p, v, current_button); p.pop(); o }) .collect(), ) .into(), Layout::Text(txt) => text(txt).into(), Layout::Button(content) => button({ p.push(0); let o = self.viewl(p, content, current_button); p.pop(); o }) .on_press(Message::ButtonPressed({ let o = *current_button; *current_button = *current_button + 1; o })) .into(), } } fn calc_layout_stats(&mut self) { self.buttons.clear(); Self::calc_layout_stats_rec(&self.layout, &mut vec![], &mut self.buttons) } fn calc_layout_stats_rec( layout: &Layout, path: &mut Vec, buttons: &mut Vec>, ) { match layout { Layout::Row(v) | Layout::Column(v) => { for (i, v) in v.iter().enumerate() { path.push(i); Self::calc_layout_stats_rec(v, path, buttons); path.pop(); } } Layout::Button(c) => { buttons.push(path.clone()); path.push(0); Self::calc_layout_stats_rec(c, path, buttons); path.pop(); } Layout::Text(_) => (), } } } #[derive(Clone, Debug)] pub enum Layout { Row(Vec), Column(Vec), Text(String), Button(Box), } impl Layout { pub fn get_mut(&mut self, path: &Vec, index: usize) -> &mut Self { if index >= path.len() { return self; } match self { Self::Row(v) => v[path[index]].get_mut(path, index + 1), Self::Column(v) => v[path[index]].get_mut(path, index + 1), Self::Button(c) => c.as_mut().get_mut(path, index + 1), Self::Text(_) => { panic!("cannot index this layout type! ({:?})", self) } } } pub fn add(&mut self, add: Layout) { match self { Self::Row(v) | Self::Column(v) => v.push(add), _ => panic!("cannot add to this layout type! ({:?})", self), } } pub fn remove(&mut self, remove: usize) { match self { Self::Row(v) | Self::Column(v) => { if remove < v.len() { v.remove(remove); } } _ => panic!("cannot add to this layout type! ({:?})", self), } } pub fn len(&self) -> usize { match self { Self::Row(v) | Self::Column(v) => v.len(), _ => panic!("cannot get len of this layout type! ({:?})", self), } } } trait DirectRead { fn nbyte(&mut self) -> Result; fn nchar(&mut self) -> Result; } impl DirectRead for T where T: Read, { fn nbyte(&mut self) -> Result { let mut b = [0]; self.read(&mut b)?; Ok(b[0]) } fn nchar(&mut self) -> Result { Ok(self.nbyte()?.into()) } }