use std::collections::HashMap;
use iced::{Command, Element, Length};
use iced::widget::{Column, scrollable, text, toggler};
use iced::widget::image::{Handle, Viewer};
use iced::widget::scrollable::{Id, Scrollable};
use iced::widget::TextInput;
use iced_aw::{TabBarStyles, TabLabel, Tabs};
use once_cell::sync::Lazy;
use crate::{ImageReference, Message};
pub(crate) struct TabSet {
pub active_tab: usize,
pub stdout_tab: StdOutTab,
pub stderr_tab: StdOutTab,
pub stdin_tab: StdInTab,
pub images_tab: ImageTab,
pub fileio_tab: StdOutTab,
}
impl TabSet {
pub(crate) fn new() -> Self {
TabSet {
active_tab: 0,
stdout_tab: StdOutTab {
name: "Stdout".to_owned(),
id: Lazy::new(Id::unique).clone(),
content: vec!(),
auto_scroll: true
},
stderr_tab: StdOutTab {
name: "Stderr".to_owned(),
id: Lazy::new(Id::unique).clone(),
content: vec!(),
auto_scroll: true
},
stdin_tab: StdInTab::new("Stdin"),
images_tab: ImageTab::new("Images"),
fileio_tab: StdOutTab {
name: "FileIO".to_owned(),
id: Lazy::new(Id::unique).clone(),
content: vec!(),
auto_scroll: true
},
}
}
pub(crate) fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::TabSelected(tab_index) => self.active_tab = tab_index,
Message::StdioAutoScrollTogglerChanged(id, value) => {
if id == self.stdout_tab.id {
self.stdout_tab.auto_scroll = value;
}
else {
self.stderr_tab.auto_scroll = value
}
if value {
return scrollable::snap_to(id,scrollable::RelativeOffset::END);
}
},
_ => {},
}
Command::none()
}
pub(crate) fn view(&self) -> Element<'_, Message> {
Tabs::new(Message::TabSelected)
.push(0, self.stdout_tab.tab_label(), self.stdout_tab.view())
.push(1, self.stderr_tab.tab_label(), self.stderr_tab.view())
.push(2, self.stdin_tab.tab_label(), self.stdin_tab.view())
.push(3, self.images_tab.tab_label(), self.images_tab.view())
.push(4, self.fileio_tab.tab_label(), self.fileio_tab.view())
.set_active_tab(&self.active_tab)
.tab_bar_style(TabBarStyles::Blue)
.into()
}
pub(crate) fn clear(&mut self) {
self.stdout_tab.clear();
self.stderr_tab.clear();
self.stdin_tab.clear();
self.images_tab.clear();
self.fileio_tab.clear();
}
}
pub trait Tab {
type Message;
fn title(&self) -> String;
fn tab_label(&self) -> TabLabel;
fn view(&self) -> Element<'_, Self::Message>;
fn clear(&mut self);
}
pub(crate) struct StdOutTab {
pub name: String,
pub id: Id,
pub content: Vec<String>,
pub auto_scroll: bool,
}
impl Tab for StdOutTab {
type Message = Message;
fn title(&self) -> String {
String::from(&self.name)
}
fn tab_label(&self) -> TabLabel {
TabLabel::Text(self.name.to_string())
}
fn view(&self) -> Element<Message> {
let text_column = Column::with_children(
self.content
.iter()
.cloned()
.map(text)
.map(Element::from)
.collect(),
)
.width(Length::Fill)
.padding(1);
let scrollable = Scrollable::new(text_column)
.height(Length::Fill)
.id(self.id.clone());
let toggler = toggler(
format!("Auto-scroll {}", self.name),
self.auto_scroll,
|v| Message::StdioAutoScrollTogglerChanged(self.id.clone(), v))
.width(Length::Shrink);
Column::new()
.push(toggler)
.push(scrollable)
.into()
}
fn clear(&mut self) {
self.content.clear();
}
}
pub(crate) struct ImageTab {
name: String,
pub images: HashMap<String, ImageReference>,
}
impl ImageTab {
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
images: Default::default(),
}
}
}
impl Tab for ImageTab {
type Message = Message;
fn title(&self) -> String {
String::from(&self.name)
}
fn tab_label(&self) -> TabLabel {
TabLabel::Text(self.name.to_string())
}
fn view(&self) -> Element<'_, Self::Message> {
let mut col = Column::new();
for image_ref in self.images.values() {
col = col.push(Viewer::new(
Handle::from_pixels( image_ref.width, image_ref.height,
image_ref.data.as_raw().clone())));
}
col.into()
}
fn clear(&mut self) {
self.images = Default::default();
}
}
pub(crate) struct StdInTab {
pub name: String,
pub id: Id,
pub content: Vec<String>,
pub cursor: usize,
pub text: String,
}
impl StdInTab {
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
id: Lazy::new(Id::unique).clone(),
content: vec!(),
cursor: 0,
text: "".into(),
}
}
pub fn text_entered(&mut self, text: String) {
self.text = text;
}
pub fn new_line(&mut self, line: String) {
self.content.push(line);
self.text = "".to_string();
}
pub fn get_line(&mut self, prompt: String) -> Option<String> {
if let Some(line) = self.content.get_mut(self.cursor) {
if !prompt.is_empty() {
line.insert_str(0, &prompt);
}
self.cursor += 1;
Some(line.to_string())
} else {
None
}
}
pub fn get_all(&mut self) -> Option<String> {
if self.content.len() > self.cursor {
let mut buf = String::new();
for line in self.cursor..self.content.len() {
if let Some(line) = self.content.get(line) {
buf.push_str(line);
}
}
self.cursor = self.content.len();
Some(buf)
} else {
None
}
}
}
impl Tab for StdInTab {
type Message = Message;
fn title(&self) -> String {
String::from(&self.name)
}
fn tab_label(&self) -> TabLabel {
TabLabel::Text(self.name.to_string())
}
fn view(&self) -> Element<'_, Self::Message> {
let text_column = Column::with_children(
self.content
.iter()
.cloned()
.map(text)
.map(Element::from)
.collect(),
)
.width(Length::Fill)
.padding(1);
let text_input = TextInput::new(
"Enter new line of Standard input", &self.text)
.on_input(Message::NewStdin)
.on_paste(Message::NewStdin)
.on_submit(Message::LineOfStdin(self.text.clone()))
.width(Length::Fill)
.padding(10);
let scrollable = Scrollable::new(text_column)
.height(Length::Fill)
.id(self.id.clone());
Column::new()
.push(scrollable)
.push(text_input)
.into()
}
fn clear(&mut self) {}
}