Skip to content

Commit

Permalink
feat: add sparkline widget for slide support
Browse files Browse the repository at this point in the history
  • Loading branch information
Chleba committed Jul 7, 2024
1 parent b8e2cb1 commit 8dbf81c
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 24 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ Main section of slide config is `box_size`. Here we set fixed size of content bo
```

### JSON: slides
`slides` property is defining content. Slide content have it's `type`, `content`, `rect` & `color`.
`slides` property is defining content. Slide content have it's `type`, `content`, `rect`, `data`, `max` & `color`.
There are few types at the moment but in the future there should be support for every widget in [Ratatui](https://github.com/ratatui-org/ratatui) library.
Supported widgets ATM:
- Image
- Block
- Paragraph
- Line
- Bigtext
- Sparkline

20 changes: 15 additions & 5 deletions src/components/slides.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl Slides {
fn make_title<'a>(slide: &SlideJson) -> BigText<'a> {
let mut title_text = "__title__".to_string();
if let Some(title) = &slide.title {
title_text = title.to_owned();
title_text = title.to_string();
}

let big_title = BigText::builder()
Expand All @@ -146,9 +146,8 @@ impl Slides {
fn make_block(title: Option<Line>) -> Block {
let s_content = ContentJson {
type_: SlideContentType::Block,
content: None,
rect: None,
color: Some("#FFDDDD".to_string()),
..Default::default()
};
let block = make_slide_block(s_content);
if let ReturnSlideWidget::Block(mut b) = block {
Expand Down Expand Up @@ -186,12 +185,13 @@ impl Slides {
fn make_slide_items<'a>(
slide: &SlideJson,
json_slides: String,
) -> Vec<(ReturnSlideWidget<'a>, Option<Rect>)> {
) -> Vec<(ReturnSlideWidget<'a>, Option<Rect>, Option<Vec<u64>>)> {
let mut slide_items = vec![];
for item in &slide.content {
slide_items.push((
make_slide_content(item.clone(), json_slides.clone()),
item.rect,
item.data.clone(),
));
}
slide_items
Expand Down Expand Up @@ -251,8 +251,14 @@ impl Component for Slides {

// -- render slide widgets
let mut img_index = 0;
for (slide, r) in slide_items {
for (slide, r, d) in slide_items {
let slide_rect = self.get_slide_rect(rect.content, r);

let mut data = vec![];
if let Some(d1) = d {
data = d1;
}

match slide {
ReturnSlideWidget::Paragraph(s) => {
f.render_widget(s, slide_rect);
Expand Down Expand Up @@ -283,6 +289,10 @@ impl Component for Slides {
ReturnSlideWidget::Block(s) => {
f.render_widget(s, slide_rect);
}
ReturnSlideWidget::Sparkline(mut s) => {
s = s.data(&data);
f.render_widget(s, slide_rect);
}
}
}
Ok(())
Expand Down
19 changes: 18 additions & 1 deletion src/enums.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use image::DynamicImage;
use ratatui::{layout::Rect, text::Line, widgets::{Block, Paragraph}};
use ratatui::{layout::Rect, text::Line, widgets::{Block, Paragraph, Sparkline}};
use serde::{Deserialize, Serialize};
use tui_big_text::BigText;

Expand All @@ -10,6 +10,7 @@ pub enum ReturnSlideWidget<'a> {
Line(Line<'a>),
Image(DynamicImage),
Block(Block<'a>),
Sparkline(Sparkline<'a>),
}

#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
Expand All @@ -19,6 +20,7 @@ pub enum SlideContentType {
Line,
Image,
Block,
Sparkline,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
Expand All @@ -28,6 +30,21 @@ pub struct ContentJson {
pub content: Option<String>,
pub rect: Option<Rect>,
pub color: Option<String>,
pub data: Option<Vec<u64>>,
pub max: Option<u64>,
}

impl Default for ContentJson {
fn default() -> Self {
Self {
type_: SlideContentType::Line,
content: None,
rect: None,
color: None,
data: None,
max: None,
}
}
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
Expand Down
85 changes: 68 additions & 17 deletions src/slide_builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{path::Path, str::FromStr};
use std::{ops::Deref, path::Path, str::FromStr, sync::Arc};

use crate::enums::{ContentJson, ReturnSlideWidget, SlideContentType, SlideJson};
use color_eyre::{eyre::Result, owo_colors::OwoColorize};
Expand All @@ -9,44 +9,70 @@ use ratatui::{
style::Stylize,
text::Line,
widgets::{
block::{self, Title}, Block, BorderType, Borders, Paragraph, WidgetRef
block::{self, Title},
Block, BorderType, Borders, Paragraph, Sparkline, WidgetRef,
},
};
use ratatui_image::{picker::Picker, Image, Resize, StatefulImage};
use tui_big_text::BigText;

pub fn get_slide_content_string(slide: ContentJson) -> String {
pub fn get_slide_content_string(slide: &ContentJson) -> String {
let mut content_str = String::from("");
if let Some(cv) = slide.content {
content_str = cv;
if let Some(cv) = &slide.content {
content_str = cv.to_string();
}
content_str
}

fn get_slide_content_color(slide: ContentJson) -> String {
if let Some(c) = slide.color {
return c;
fn get_slide_content_color(slide: &ContentJson) -> String {
if let Some(c) = &slide.color {
return c.to_owned();
}
String::from("#FF0000")
}

fn get_slide_content_max(slide: &ContentJson) -> u64 {
if let Some(c) = slide.max {
return c;
}
10
}

// fn get_slide_content_data(slide: &ContentJson) -> Vec<u64> {
// if let Some(c) = slide.data {
// return c.to_vec();
// }
// vec![0]
// }

// -------------
// -- PARAGRAPH
// -------------
fn make_slide_paragraph<'a>(slide: ContentJson) -> ReturnSlideWidget<'a> {
let content = get_slide_content_string(slide.clone());
let color = get_slide_content_color(slide);
ReturnSlideWidget::Paragraph(Paragraph::new(content).style(Style::default().fg(Color::from_str(&color).unwrap())))
let content = get_slide_content_string(&slide);
let color = get_slide_content_color(&slide);
ReturnSlideWidget::Paragraph(
Paragraph::new(content).style(Style::default().fg(Color::from_str(&color).unwrap())),
)
}

// -------------
// -- LINE
// -------------
fn make_slide_line<'a>(slide: ContentJson) -> ReturnSlideWidget<'a> {
let content = get_slide_content_string(slide.clone());
let color = get_slide_content_color(slide);
let content = get_slide_content_string(&slide);
let color = get_slide_content_color(&slide);
ReturnSlideWidget::Line(
Line::from(content)
.style(Style::default().fg(Color::from_str(&color).unwrap_or(Color::Blue))),
)
}

// -------------
// -- BIGTEXT
// -------------
fn make_slide_bigtext<'a>(slide: ContentJson) -> ReturnSlideWidget<'a> {
let content = get_slide_content_string(slide);
let content = get_slide_content_string(&slide);
let lines: Vec<Line> = content
.split('\n')
.map(|s| Line::from(s.to_string()))
Expand All @@ -61,20 +87,26 @@ fn make_slide_bigtext<'a>(slide: ContentJson) -> ReturnSlideWidget<'a> {
)
}

// -------------
// -- IMAGE
// -------------
pub fn make_slide_image<'a>(slide: ContentJson, slide_path: String) -> ReturnSlideWidget<'a> {
let f_path = Path::new(&slide_path);
let img_path = f_path.parent().unwrap();
let content = get_slide_content_string(slide);
let content = get_slide_content_string(&slide);
let dyn_img = image::io::Reader::open(img_path.join(content))
.unwrap()
.decode()
.unwrap();
ReturnSlideWidget::Image(dyn_img)
}

// -------------
// -- BLOCK
// -------------
pub fn make_slide_block<'a>(slide: ContentJson) -> ReturnSlideWidget<'a> {
let content = get_slide_content_string(slide.clone());
let color = get_slide_content_color(slide);
let content = get_slide_content_string(&slide);
let color = get_slide_content_color(&slide);
ReturnSlideWidget::Block(
Block::default()
.borders(Borders::ALL)
Expand All @@ -87,6 +119,24 @@ pub fn make_slide_block<'a>(slide: ContentJson) -> ReturnSlideWidget<'a> {
)
}

// -------------
// -- SPARKLINE
// -------------
pub fn make_slide_sparkline<'a>(slide: ContentJson) -> ReturnSlideWidget<'a> {
let content = get_slide_content_string(&slide);
let color = get_slide_content_color(&slide);
let max = get_slide_content_max(&slide);
// let data = get_slide_content_data(&slide).to_owned();

ReturnSlideWidget::Sparkline(
Sparkline::default()
// .data(&data)
// .data(data)
.max(max)
.style(Style::default().red().on_black()),
)
}

pub fn make_slide_content<'a>(
slide_content: ContentJson,
slide_path: String,
Expand All @@ -97,5 +147,6 @@ pub fn make_slide_content<'a>(
SlideContentType::Line => make_slide_line(slide_content),
SlideContentType::Image => make_slide_image(slide_content, slide_path),
SlideContentType::Block => make_slide_block(slide_content),
SlideContentType::Sparkline => make_slide_sparkline(slide_content),
}
}
Binary file added talk_example/images/burning_wizard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added talk_example/images/magical_stone.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions talk_example/slides.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@
"content": [
{ "type": "Image", "content": "./images/rust.jpg", "rect": { "x": 27, "y": 10, "width": 34, "height": 19 } }
]
},

{
"title": "learning",
"content": [
{ "type": "Line", "content": "rustlings" }
]
},
{
"title": "SPARKLINE",
"content": [
{"type": "Sparkline", "data": [0, 1, 2, 3, 4, 5, 10, 5, 5, 4, 1, 3, 2, 6, 7, 3, 9, 7], "max": 10, "direction": "RightToLeft", "rect": {"x": 10, "y": 10, "width": 50, "height": 20}}
]
}
]
}

0 comments on commit 8dbf81c

Please sign in to comment.