output.rs
1 //! The view for interpreter's output. 2 3 use iced::{ 4 alignment, font, 5 widget::{ 6 button, checkbox, column, container, 7 container::Appearance as ContainerAppearance, 8 horizontal_rule, horizontal_space, row, 9 rule::{self, Appearance as RuleAppearance}, 10 scrollable, text, Column, Row, 11 }, 12 Border, Font, 13 }; 14 use uuid::Uuid; 15 16 use super::Message; 17 use crate::interpreter::{LinkOutput, ModuleResult as InterpreterResult, Output}; 18 19 #[derive(Debug)] 20 pub(crate) struct OutputView { 21 id: Uuid, 22 width: f32, 23 output: Option<InterpreterResult<Vec<Output>>>, 24 is_pinned: bool, 25 cmd: String, 26 } 27 28 impl OutputView { 29 pub(crate) fn new() -> Self { 30 Self { 31 id: Uuid::new_v4(), 32 width: 0.0, 33 output: None, 34 is_pinned: false, 35 cmd: String::new(), 36 } 37 } 38 39 pub(crate) fn id(&self) -> Uuid { 40 self.id 41 } 42 43 pub(crate) fn with_width(mut self, width: f32) -> Self { 44 self.width = width; 45 self 46 } 47 48 pub(crate) fn set_output(&mut self, output: Option<InterpreterResult<Vec<Output>>>) { 49 self.output = output; 50 } 51 52 pub(crate) fn set_cmd(&mut self, cmd: &str) { 53 self.cmd = cmd.to_owned(); 54 } 55 56 pub(crate) fn set_pinned(&mut self, is_pinned: bool) { 57 self.is_pinned = is_pinned; 58 } 59 60 pub(crate) fn view(&self) -> iced::Element<'_, Message> { 61 if let Some(output) = &self.output { 62 return match output { 63 Ok(msg) => self.view_output(msg), 64 Err(msg) => self.view_error(msg.to_string()), 65 }; 66 } 67 68 container("").width(self.width).into() 69 } 70 71 fn view_output(&self, outputs: &[Output]) -> iced::Element<'_, Message> { 72 let mut elements = vec![self.view_output_header(&self.cmd)]; 73 for output in outputs { 74 elements.push(self.parse_output(output)); 75 } 76 77 container(column(elements)) 78 .width(self.width) 79 .padding(20.0) 80 .into() 81 } 82 83 fn parse_output(&self, output: &Output) -> iced::Element<'_, Message> { 84 match output { 85 Output::Paragraph(value) => self.view_paragraph(value), 86 Output::Header(value) => self.view_header(value), 87 Output::Divider => horizontal_rule(20.0).into(), 88 Output::List(value) => self.view_grid(value, 8), 89 Output::Link(value) => self.view_link(value), 90 } 91 } 92 93 fn view_output_header(&self, title: &str) -> iced::Element<'_, Message> { 94 container( 95 row(vec![ 96 self.view_header(title), 97 horizontal_space().into(), 98 container( 99 checkbox("pin", self.is_pinned) 100 .on_toggle(|v| Message::SetPinOutput((self.id, v))), 101 ) 102 .padding([0.0, 40.0]) 103 .into(), 104 ]) 105 .align_items(iced::Alignment::Center), 106 ) 107 .style(ContainerAppearance { 108 background: Some(iced::Color::from_rgba(0.0, 0.0, 0.0, 0.2).into()), 109 ..Default::default() 110 }) 111 .into() 112 } 113 114 fn view_grid(&self, list: &[Output], per_row: usize) -> iced::Element<'_, Message> { 115 let mut top_row = Row::new().spacing(10.0); 116 let mut column = Column::new() 117 .spacing(10.0) 118 .width(self.width) 119 .align_items(iced::Alignment::Start); 120 121 for (n, output) in list.iter().enumerate() { 122 if n % per_row == 0 { 123 column = column.push(top_row); 124 top_row = Row::new().spacing(10.0); 125 continue; 126 } 127 128 // top_row = top_row.push(horizontal_space()); 129 top_row = top_row.push(self.parse_output(output)); 130 } 131 132 column.push(top_row).into() 133 } 134 135 fn view_link(&self, link: &LinkOutput) -> iced::Element<'_, Message> { 136 button( 137 text(&link.text) 138 .font(Font::MONOSPACE) 139 .horizontal_alignment(alignment::Horizontal::Center), 140 ) 141 .width(link.width) 142 .style(iced::theme::Button::Text) 143 .on_press(Message::SendCommand(link.cmd.to_owned())) 144 .into() 145 } 146 147 fn view_paragraph(&self, value: &str) -> iced::Element<'_, Message> { 148 container(text(value).font(Font::MONOSPACE)) 149 .padding(20.0) 150 .into() 151 } 152 153 fn view_header(&self, value: &str) -> iced::Element<'_, Message> { 154 let mut style = Font::MONOSPACE; 155 style.weight = font::Weight::Bold; 156 container(text(value).font(style).size(24.0)) 157 .padding([20.0, 0.0]) 158 .into() 159 } 160 161 fn view_error(&self, msg: String) -> iced::Element<'_, Message> { 162 let text = text(msg).font(Font::MONOSPACE).width(self.width); 163 let mut style = ContainerAppearance::default(); 164 style.border = Border { 165 color: [1.0, 0.0, 0.0].into(), 166 width: 1.0, 167 ..Default::default() 168 }; 169 container(text) 170 .width(self.width) 171 .style(style) 172 .padding(20.0) 173 .into() 174 } 175 }