Skip to content

Commit

Permalink
Spawn task for each header
Browse files Browse the repository at this point in the history
Updated ratatui-image to make the picker non-mut. Now we don't need
any Mutexes.
  • Loading branch information
benjajaja committed Dec 24, 2024
1 parent 469ac2d commit 4875e4f
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 155 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ crossterm = { version = "0.28.1", features = ["event-stream"] }
font-loader = "0.11.0"
image = "0.25.2"
ratatui = "^0.29.0"
ratatui-image = { version = "4.0.2-dev", features = ["serde"] }
ratatui-image = { version = "4.1.0", features = ["serde"] }
rusttype = "0.9.3"
serde = "^1.0"
clap = { version = "4.5.21", features = ["cargo"] }
Expand Down
71 changes: 34 additions & 37 deletions src/fontpicker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
Error,
};

pub fn set_up_font(picker: &mut Picker, bg: Option<[u8; 4]>) -> Result<Option<String>, Error> {
pub async fn set_up_font(
picker: &mut Picker,
bg: Option<[u8; 4]>,
) -> Result<Option<String>, Error> {
let mut terminal = ratatui::init_with_options(ratatui::TerminalOptions {
viewport: ratatui::Viewport::Inline(6),
});
Expand All @@ -24,6 +27,7 @@ pub fn set_up_font(picker: &mut Picker, bg: Option<[u8; 4]>) -> Result<Option<St
.collect();

let mut last_rendered: Option<(String, Protocol)> = None;
let mut inner_width = 0;

loop {
let first_match = find_first_match(&all_fonts, &input);
Expand All @@ -45,6 +49,7 @@ pub fn set_up_font(picker: &mut Picker, bg: Option<[u8; 4]>) -> Result<Option<St
.title("Enter font name (Tab: complete, Esc: abort, Enter: confirm):")
.borders(ratatui::widgets::Borders::ALL);
let inner_area = block.inner(area);
inner_width = inner_area.width;
f.render_widget(block, area);

if let Some(first_match) = first_match {
Expand All @@ -58,46 +63,38 @@ pub fn set_up_font(picker: &mut Picker, bg: Option<[u8; 4]>) -> Result<Option<St
inner_area,
);

if let Some((_, ref mut proto)) = last_rendered {
let img = Image::new(proto);
let mut area = inner_area;
area.y += 1;
area.height = 2;
f.render_widget(img, area);
}
})?;

if inner_width > 0 && (last_rendered.is_none() || last_rendered.clone().unwrap().0 != input)
{
if let Some(font) = font {
match &mut last_rendered {
Some((last_input, ref mut proto)) if *last_input == input => {
let img = Image::new(proto);
let mut area = inner_area;
area.y += 1;
area.height = 2;
f.render_widget(img, area);
}
_ => {
let spans = vec!["The fox jumped over the goat or something".into()];
if let Ok(sources) = header_source(
&mut Renderer {
picker: *picker,
font,
bg,
},
inner_area.width,
0,
Line::from(spans).to_string(),
1,
false,
) {
if let Some(source) = sources.into_iter().next() {
if let WidgetSourceData::Image(mut proto) = source.source {
let img = Image::new(&mut proto);
let mut area = inner_area;
area.y += 1;
area.height = 2;
f.render_widget(img, area);
last_rendered = Some((input.clone(), proto));
}
}
}
let spans = vec!["The fox jumped over the goat or something".into()];
let sources = header_source(
&Renderer::new(*picker, font, bg),
inner_width,
0,
Line::from(spans).to_string(),
1,
false,
)
.await?;

// Just render the first line if it got split.
if let Some(source) = sources.into_iter().next() {
if let WidgetSourceData::Image(proto) = source.source {
last_rendered = Some((input.clone(), proto));
}
}
} else {
last_rendered = None;
}
})?;
}

if event::poll(std::time::Duration::from_millis(100))? {
if let Event::Key(KeyEvent {
code, modifiers, ..
Expand Down
202 changes: 103 additions & 99 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use std::{
io::{self, Read},
os::fd::IntoRawFd,
path::{Path, PathBuf},
sync::mpsc::{self, Receiver, Sender},
sync::{
mpsc::{self, Receiver, Sender},
Arc,
},
time::Duration,
};

Expand All @@ -29,6 +32,7 @@ use ratatui_image::Image;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use setup::setup_graphics;
use tokio::sync::RwLock;
use widget_sources::{header_source, image_source, WidgetSource, WidgetSourceData};

mod config;
Expand Down Expand Up @@ -125,60 +129,56 @@ async fn start(matches: &ArgMatches) -> Result<(), Error> {
}

let force_setup = *matches.get_one("setup").unwrap_or(&false);
let mut renderer = match setup_graphics(config.font_family, force_setup) {
let renderer = match setup_graphics(config.font_family, force_setup).await {
Ok(Some(renderer)) => renderer,
Ok(None) => return Err(Error::UserAbort("cancelled setup")),
Err(err) => return Err(err),
};
let _deep_fry = *matches.get_one("deep").unwrap_or(&false);

let bg = renderer.bg;

let (cmd_tx, cmd_rx) = mpsc::channel::<ImgCmd>();
let (event_tx, event_rx) = mpsc::channel::<(u16, Event)>();

let _deep_fry = *matches.get_one("deep").unwrap_or(&false);

let event_image_tx = event_tx.clone();
let parse_handle = tokio::spawn(async move {
let basepath = basepath.clone();
let mut client = Client::new();
let client = Arc::new(RwLock::new(Client::new()));
let renderer = Arc::new(renderer);
for cmd in cmd_rx {
match cmd {
ImgCmd::Header(index, width, tier, text) => {
let task_tx = event_image_tx.clone();
task_tx.send((
width,
Event::Update(header_source(
&mut renderer,
width,
index,
text,
tier,
false,
)?),
))?;
let r = renderer.clone();
tokio::spawn(async move {
let header = header_source(&r, width, index, text, tier, false).await?;
task_tx.send((width, Event::Update(header)))?;
Ok::<(), Error>(())
});
}
ImgCmd::UrlImage(index, width, url, text, _title) => {
match image_source(
&mut renderer.picker,
width,
&basepath,
&mut client,
index,
&url,
false,
)
.await
{
Ok(source) => event_image_tx.send((width, Event::Update(vec![source])))?,
Err(Error::UnknownImage(index, link)) => event_image_tx.send((
width,
Event::Update(vec![WidgetSource::image_unknown(index, link, text)]),
))?,
Err(_) => event_image_tx.send((
width,
Event::Update(vec![WidgetSource::image_unknown(index, url, text)]),
))?,
}
let task_tx = event_image_tx.clone();
let r = renderer.clone();
let basepath = basepath.clone();
let client = client.clone();
tokio::spawn(async move {
let picker = r.picker;
match image_source(&picker, width, &basepath, client, index, &url, false)
.await
{
Ok(source) => task_tx.send((width, Event::Update(vec![source])))?,
Err(Error::UnknownImage(index, link)) => task_tx.send((
width,
Event::Update(vec![WidgetSource::image_unknown(index, link, text)]),
))?,
Err(_) => task_tx.send((
width,
Event::Update(vec![WidgetSource::image_unknown(index, url, text)]),
))?,
}
Ok::<(), Error>(())
});
}
};
}
Expand Down Expand Up @@ -328,74 +328,78 @@ fn run<'a>(mut terminal: DefaultTerminal, mut model: Model<'a, 'a>) -> Result<()

loop {
let mut had_events = false;
if let Ok((id, ev)) = model.rx.try_recv() {
if id == inner_width {
had_events = true;
match ev {
Event::Parsed(source) => {
model.sources.push(source);
}
Event::Update(updates) => {
if let Some(index) = updates.first().map(|s| s.index) {
let mut first_position = None;
let mut i = 0;
model.sources.retain(|w| {
if w.index == index {
first_position = match first_position {
None => Some((i, i)),
Some((f, _)) => Some((f, i)),
};
return false;
loop {
if let Ok((id, ev)) = model.rx.try_recv() {
if id == inner_width {
had_events = true;
match ev {
Event::Parsed(source) => {
model.sources.push(source);
}
Event::Update(updates) => {
if let Some(index) = updates.first().map(|s| s.index) {
let mut first_position = None;
let mut i = 0;
model.sources.retain(|w| {
if w.index == index {
first_position = match first_position {
None => Some((i, i)),
Some((f, _)) => Some((f, i)),
};
return false;
}
i += 1;
true
});

if let Some((from, to)) = first_position {
model.sources.splice(from..to, updates);
}
i += 1;
true
});

if let Some((from, to)) = first_position {
model.sources.splice(from..to, updates);
debug_assert!(
first_position.is_some(),
"Update #{:?} not found anymore",
index,
);
}
debug_assert!(
first_position.is_some(),
"Update #{:?} not found anymore",
}
Event::ParseImage(index, url, text, title) => {
model.tx.send(ImgCmd::UrlImage(
index,
inner_width,
url.clone(),
text,
title,
))?;
model.sources.push(WidgetSource {
index,
);
height: 1,
source: WidgetSourceData::Line(Line::from(format!(
"![Loading...]({url})"
))),
});
}
Event::ParseHeader(index, tier, spans) => {
let line = Line::from(spans);
let inner_width = match model.padding {
Padding::None => screen_width,
Padding::Empty | Padding::Border => screen_width - 2,
};
model.tx.send(ImgCmd::Header(
index,
inner_width,
tier,
line.to_string(),
))?;
model.sources.push(WidgetSource {
index,
height: 2,
source: WidgetSourceData::Line(line),
});
}
}
Event::ParseImage(index, url, text, title) => {
model.tx.send(ImgCmd::UrlImage(
index,
inner_width,
url.clone(),
text,
title,
))?;
model.sources.push(WidgetSource {
index,
height: 1,
source: WidgetSourceData::Line(Line::from(format!(
"![Loading...]({url})"
))),
});
}
Event::ParseHeader(index, tier, spans) => {
let line = Line::from(spans);
let inner_width = match model.padding {
Padding::None => screen_width,
Padding::Empty | Padding::Border => screen_width - 2,
};
model.tx.send(ImgCmd::Header(
index,
inner_width,
tier,
line.to_string(),
))?;
model.sources.push(WidgetSource {
index,
height: 2,
source: WidgetSourceData::Line(line),
});
}
}
} else {
break;
}
}

Expand Down
Loading

0 comments on commit 4875e4f

Please # to comment.