mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 22:37:46 +01:00
445 lines
15 KiB
Rust
Executable File
445 lines
15 KiB
Rust
Executable File
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::{MyLib, MyLibTask, VData, VDataEnum, VSingleType, VType};
|
|
|
|
/*
|
|
|
|
Path: Vec<usize>
|
|
|
|
*/
|
|
|
|
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<usize> = 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<usize> = 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<usize>, Layout),
|
|
LSet(Vec<usize>, Layout),
|
|
LRemove(Vec<usize>),
|
|
}
|
|
|
|
struct App {
|
|
title: String,
|
|
recv: mpsc::Receiver<Task>,
|
|
sender: mpsc::Sender<MessageAdv>,
|
|
buttons: Vec<Vec<usize>>,
|
|
layout: Layout,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
enum Message {
|
|
Tick,
|
|
ButtonPressed(usize),
|
|
}
|
|
enum MessageAdv {
|
|
ButtonPressed(Vec<VData>),
|
|
}
|
|
|
|
impl Application for App {
|
|
type Executor = executor::Default;
|
|
type Message = Message;
|
|
type Theme = Theme;
|
|
type Flags = (mpsc::Receiver<Task>, mpsc::Sender<MessageAdv>);
|
|
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
|
(
|
|
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<Self::Message> {
|
|
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::none()
|
|
}
|
|
fn subscription(&self) -> Subscription<Message> {
|
|
time::every(Duration::from_millis(10)).map(|_| Message::Tick)
|
|
}
|
|
fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
|
|
self.viewl(&mut vec![], &self.layout, &mut 0)
|
|
}
|
|
}
|
|
impl App {
|
|
fn viewl(
|
|
&self,
|
|
p: &mut Vec<usize>,
|
|
l: &Layout,
|
|
current_button: &mut usize,
|
|
) -> Element<'_, <Self as Application>::Message, Renderer<<Self as Application>::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<usize>,
|
|
buttons: &mut Vec<Vec<usize>>,
|
|
) {
|
|
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<Self>),
|
|
Column(Vec<Self>),
|
|
Text(String),
|
|
Button(Box<Self>),
|
|
}
|
|
impl Layout {
|
|
pub fn get_mut(&mut self, path: &Vec<usize>, 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<u8, io::Error>;
|
|
fn nchar(&mut self) -> Result<char, io::Error>;
|
|
}
|
|
impl<T> DirectRead for T
|
|
where
|
|
T: Read,
|
|
{
|
|
fn nbyte(&mut self) -> Result<u8, io::Error> {
|
|
let mut b = [0];
|
|
self.read(&mut b)?;
|
|
Ok(b[0])
|
|
}
|
|
fn nchar(&mut self) -> Result<char, io::Error> {
|
|
Ok(self.nbyte()?.into())
|
|
}
|
|
}
|