/ src / app / output.rs
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  }