browse: hugely improve text rendering performance

This commit is contained in:
FliegendeWurst 2021-05-17 23:00:42 +02:00 committed by Arne Keller
parent 3de317af35
commit 175695b22c

View File

@ -3,7 +3,7 @@
use std::{cell::RefCell, cmp, collections::{HashMap, HashSet}, env, fmt::Display, io, sync::{Arc, atomic::{AtomicBool, Ordering}}}; use std::{cell::RefCell, cmp, collections::{HashMap, HashSet}, env, fmt::Display, io, sync::{Arc, atomic::{AtomicBool, Ordering}}};
use std::result::Result as StdResult; use std::result::Result as StdResult;
use cursive::{Cursive, Vec2, WrapMethod, traits::Finder, view::ViewWrapper}; use cursive::{Cursive, Vec2, WrapMethod, view::ViewWrapper};
use cursive::align::HAlign; use cursive::align::HAlign;
use cursive::event::{Event, Key}; use cursive::event::{Event, Key};
use cursive::traits::Identifiable; use cursive::traits::Identifiable;
@ -14,7 +14,7 @@ use inboxid::*;
use io::Write; use io::Write;
use itertools::Itertools; use itertools::Itertools;
use log::error; use log::error;
use mailparse::{MailHeaderMap, ParsedMail, parse_mail}; use mailparse::{MailHeaderMap, ParsedMail};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use petgraph::{EdgeDirection, graph::{DiGraph, NodeIndex}, visit::{Dfs, IntoNodeReferences}}; use petgraph::{EdgeDirection, graph::{DiGraph, NodeIndex}, visit::{Dfs, IntoNodeReferences}};
use rusqlite::params; use rusqlite::params;
@ -218,9 +218,6 @@ fn show_listing(mailbox: &str) -> Result<()> {
siv.call_on_name("mail", |view: &mut MailPartView| { siv.call_on_name("mail", |view: &mut MailPartView| {
view.set_part(mail); view.set_part(mail);
}); });
siv.call_on_name("mail_scroller", |view: &mut MailScrollerView| {
view.scroll_to_top();
});
} }
}; };
tree.set_on_submit(|siv, _row| { tree.set_on_submit(|siv, _row| {
@ -277,11 +274,11 @@ fn show_listing(mailbox: &str) -> Result<()> {
} }
}); });
}); });
let tree_resized = ResizedView::new(SizeConstraint::AtMost(120), SizeConstraint::Free, tree); let tree_resized = ResizedView::new(SizeConstraint::Fixed(120), SizeConstraint::Full, tree);
let mail_info = MailInfoView::new().with_name("mail_info"); let mail_info = MailInfoView::new().with_name("mail_info");
let mail_content = MailPartView::empty().with_name("mail"); let mail_content = MailPartView::empty().with_name("mail");
static MAIL_FULLSCREEN: AtomicBool = AtomicBool::new(false); static MAIL_FULLSCREEN: AtomicBool = AtomicBool::new(false);
let dummy = std::rc::Rc::new(RefCell::new(Some(OnEventView::new(MailView::empty().with_name("dummy")).scrollable()))); // TODO dummy content let dummy = std::rc::Rc::new(RefCell::new(Some(OnEventView::new(MailView::empty().with_name("dummy")))));
let dummy_ = dummy.clone(); let dummy_ = dummy.clone();
let mail_content = OnEventView::new(mail_content) let mail_content = OnEventView::new(mail_content)
.on_event('f', move |s| { .on_event('f', move |s| {
@ -290,8 +287,8 @@ fn show_listing(mailbox: &str) -> Result<()> {
let layer = s.pop_layer().unwrap(); let layer = s.pop_layer().unwrap();
if let Ok(textview) = layer.downcast::<ResizedView<MailScrollerView>>() { if let Ok(textview) = layer.downcast::<ResizedView<MailScrollerView>>() {
let mut it = textview.into_inner().unwrap_or_else(|_| panic!("?")); let mut it = textview.into_inner().unwrap_or_else(|_| panic!("?"));
it.set_show_scrollbars(true); it.get_inner_mut().get_mut().text.as_mut().unwrap().set_show_scrollbars(true);
it.get_inner_mut().get_inner_mut().get_mut().set_wrap_method(WrapMethod::XiUnicode); it.get_inner_mut().get_mut().set_wrap_method(WrapMethod::XiUnicode);
dummy__.borrow_mut().replace(it); dummy__.borrow_mut().replace(it);
s.call_on_name("mail_scroller", move |this: &mut MailScrollerView| { s.call_on_name("mail_scroller", move |this: &mut MailScrollerView| {
std::mem::swap(dummy__.borrow_mut().as_mut().unwrap(), this); std::mem::swap(dummy__.borrow_mut().as_mut().unwrap(), this);
@ -303,14 +300,14 @@ fn show_listing(mailbox: &str) -> Result<()> {
std::mem::swap(dummy__.borrow_mut().as_mut().unwrap(), this); std::mem::swap(dummy__.borrow_mut().as_mut().unwrap(), this);
}); });
let mut it = dummy_.borrow_mut().take().unwrap(); let mut it = dummy_.borrow_mut().take().unwrap();
it.set_show_scrollbars(false); it.get_inner_mut().get_mut().text.as_mut().unwrap().set_show_scrollbars(false);
it.get_inner_mut().get_inner_mut().get_mut().set_wrap_method(WrapMethod::Newlines); it.get_inner_mut().get_mut().set_wrap_method(WrapMethod::Newlines);
eprintln!("adding fullscreen layer!"); eprintln!("adding fullscreen layer!");
s.add_fullscreen_layer(ResizedView::with_full_screen(it)); s.add_fullscreen_layer(ResizedView::with_full_screen(it));
MAIL_FULLSCREEN.store(true, Ordering::SeqCst); MAIL_FULLSCREEN.store(true, Ordering::SeqCst);
} }
}); });
let mail_content: MailScrollerView = mail_content.scrollable(); let mail_content: MailScrollerView = mail_content;
let mail_content = mail_content.with_name("mail_scroller"); let mail_content = mail_content.with_name("mail_scroller");
let mut mail_part_select = TreeView::<MailPart>::new(); let mut mail_part_select = TreeView::<MailPart>::new();
mail_part_select.set_on_select(|siv, row| { mail_part_select.set_on_select(|siv, row| {
@ -325,11 +322,11 @@ fn show_listing(mailbox: &str) -> Result<()> {
siv.focus_name("mail").unwrap(); siv.focus_name("mail").unwrap();
}); });
let mail_wrapper = LinearLayout::vertical() let mail_wrapper = LinearLayout::vertical()
.child(ResizedView::with_full_width(Panel::new(mail_info).title("Mail"))) .child(ResizedView::new(SizeConstraint::Full, SizeConstraint::Fixed(5), Panel::new(mail_info).title("Mail")))
.child(ResizedView::with_full_height(mail_content)) .child(ResizedView::with_full_screen(mail_content))
.child(Panel::new(mail_part_select.with_name("part_select")) .child(Panel::new(mail_part_select.with_name("part_select"))
.title("Multipart selection")); .title("Multipart selection"));
let mail_content_resized = ResizedView::new(SizeConstraint::Full, SizeConstraint::Free, mail_wrapper); let mail_content_resized = ResizedView::new(SizeConstraint::Fixed(127), SizeConstraint::Full, mail_wrapper);
let main = LinearLayout::horizontal() let main = LinearLayout::horizontal()
.child(tree_resized) .child(tree_resized)
.child(mail_content_resized); .child(mail_content_resized);
@ -387,6 +384,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
// manual event loop (to scroll to end of ScrollView) // manual event loop (to scroll to end of ScrollView)
let mut siv = siv.into_runner(cursive::backends::termion::Backend::init()?); let mut siv = siv.into_runner(cursive::backends::termion::Backend::init()?);
siv.set_autorefresh(false);
siv.refresh(); siv.refresh();
siv.call_on_name("tree_scroller", |tree: &mut ScrollView<NamedView<MailTreeView>>| { siv.call_on_name("tree_scroller", |tree: &mut ScrollView<NamedView<MailTreeView>>| {
tree.on_event(Event::Key(Key::End)) tree.on_event(Event::Key(Key::End))
@ -399,7 +397,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
Ok(()) Ok(())
} }
type MailScrollerView = ScrollView<OnEventView<NamedView<MailView>>>; type MailScrollerView = OnEventView<NamedView<MailView>>;
type MailView = MailPartView; type MailView = MailPartView;
type MailTreeView<'a> = TreeView<&'a EasyMail<'a>>; type MailTreeView<'a> = TreeView<&'a EasyMail<'a>>;
@ -427,8 +425,10 @@ impl TreeEntry for MailPart {}
struct MailPartView { struct MailPartView {
part: Option<&'static ParsedMail<'static>>, part: Option<&'static ParsedMail<'static>>,
wrap: WrapMethod, wrap: WrapMethod,
text: Option<TextView>, text: Option<ScrollView<TextView>>,
cached_size: Option<Vec2> cached_size: Option<Vec2>,
expected_text_height: Option<usize>,
layouted_text_with_scroll: bool
} }
impl MailPartView { impl MailPartView {
@ -437,13 +437,15 @@ impl MailPartView {
part: None, part: None,
wrap: WrapMethod::XiUnicode, wrap: WrapMethod::XiUnicode,
text: None, text: None,
cached_size: None cached_size: None,
expected_text_height: None,
layouted_text_with_scroll: false
} }
} }
fn set_wrap_method(&mut self, wrap: WrapMethod) { fn set_wrap_method(&mut self, wrap: WrapMethod) {
if let Some(text) = self.text.as_mut() { if let Some(text) = self.text.as_mut() {
text.set_wrap_method(wrap); text.get_inner_mut().set_wrap_method(wrap);
} }
self.wrap = wrap; self.wrap = wrap;
} }
@ -452,6 +454,8 @@ impl MailPartView {
self.part = Some(part); self.part = Some(part);
self.text = None; self.text = None;
self.cached_size = None; self.cached_size = None;
self.expected_text_height = None;
self.layouted_text_with_scroll = false;
} }
fn setup_text(&mut self, size: Vec2) { fn setup_text(&mut self, size: Vec2) {
@ -461,20 +465,21 @@ impl MailPartView {
let part = self.part.unwrap(); let part = self.part.unwrap();
let body = if part.ctype.mimetype == "text/html" { let body = if part.ctype.mimetype == "text/html" {
let html = part.get_body().unwrap(); let html = part.get_body().unwrap();
eprintln!("HTML layout using {} width", size.x); eprintln!("HTML layout using {} width, length {:?}", size.x, html.len());
html2text::from_read(html.as_bytes(), size.x) html2text::from_read(html.as_bytes(), size.x)
} else { } else {
part.get_body().unwrap() part.get_body().unwrap()
}; };
let mut text = TextView::new(body); let mut text = TextView::new(body);
text.set_wrap_method(self.wrap); text.set_wrap_method(self.wrap);
self.text = Some(text); self.text = Some(text.scrollable());
} }
} }
impl View for MailPartView { impl View for MailPartView {
fn draw(&self, printer: &cursive::Printer) { fn draw(&self, printer: &cursive::Printer) {
eprintln!("drawing with {:?}", printer.output_size); eprintln!("drawing with {:?}", printer.output_size);
eprintln!("have text {:?}", self.text.is_some());
if let Some(text) = self.text.as_ref() { if let Some(text) = self.text.as_ref() {
text.draw(printer) text.draw(printer)
} else { } else {
@ -484,8 +489,18 @@ impl View for MailPartView {
fn layout(&mut self, given_size: Vec2) { fn layout(&mut self, given_size: Vec2) {
eprintln!("layout called with {:?}", given_size); eprintln!("layout called with {:?}", given_size);
if self.cached_size.is_some() && self.cached_size != Some(given_size) { if self.cached_size.is_some() {
if self.cached_size != Some(given_size) {
self.setup_text(given_size); self.setup_text(given_size);
} else {
if !self.layouted_text_with_scroll && self.expected_text_height.unwrap_or(0) > given_size.y {
eprintln!("reconsidering given {:?}", given_size);
self.setup_text(given_size.map_x(|x| x-2));
self.layouted_text_with_scroll = true;
} else {
return;
}
}
} }
self.cached_size = Some(given_size); self.cached_size = Some(given_size);
if let Some(text) = self.text.as_mut() { if let Some(text) = self.text.as_mut() {
@ -501,14 +516,10 @@ impl View for MailPartView {
} }
fn required_size(&mut self, constraint: Vec2) -> Vec2 { fn required_size(&mut self, constraint: Vec2) -> Vec2 {
// just use all of the available space if self.expected_text_height.is_none() && self.text.is_some() {
if let Some(text) = self.text.as_mut() { self.expected_text_height = Some(self.text.as_mut().unwrap().required_size(constraint).y);
let text_constraint = text.required_size(constraint);
let (x, y) = text_constraint.pair();
(x.max(constraint.x), y.max(constraint.y)).into()
} else {
constraint
} }
constraint
} }
fn on_event(&mut self, ev: Event) -> cursive::event::EventResult { fn on_event(&mut self, ev: Event) -> cursive::event::EventResult {