finishing touches and benchmarks

This commit is contained in:
Evan Almloff committed 2022-03-30 20:45:41 -05:00
1 parent 05d04165ba
commit 5b25500c0b
15 files changed
+305 -206

No files matched your search

+6 -8
View File
@@ -5,10 +5,7 @@ use dioxus_core::*;
use fxhash::{FxHashMap, FxHashSet};
use dioxus_html::{on::*, KeyCode};
use dioxus_native_core::{
client_tree::{ClientTree, TreeNode},
layout::StretchLayout,
};
use dioxus_native_core::real_dom::{RealDom, TreeNode};
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
use std::{
any::Any,
@@ -19,6 +16,7 @@ use std::{
};
use stretch2::{prelude::Layout, Stretch};
use crate::layout::StretchLayout;
use crate::style_attributes::StyleModifier;
// a wrapper around the input state for easier access
@@ -169,7 +167,7 @@ impl InnerInputState {
evts: &mut Vec<EventCore>,
resolved_events: &mut Vec<UserEvent>,
layout: &Stretch,
tree: &mut ClientTree<StretchLayout, StyleModifier>,
tree: &mut RealDom<StretchLayout, StyleModifier>,
) {
let previous_mouse = self
.mouse
@@ -194,7 +192,7 @@ impl InnerInputState {
previous_mouse: Option<(MouseData, Vec<u16>)>,
resolved_events: &mut Vec<UserEvent>,
layout: &Stretch,
tree: &mut ClientTree<StretchLayout, StyleModifier>,
tree: &mut RealDom<StretchLayout, StyleModifier>,
) {
struct Data<'b> {
new_pos: (i32, i32),
@@ -219,7 +217,7 @@ impl InnerInputState {
will_bubble: &mut FxHashSet<ElementId>,
resolved_events: &mut Vec<UserEvent>,
node: &TreeNode<StretchLayout, StyleModifier>,
tree: &ClientTree<StretchLayout, StyleModifier>,
tree: &RealDom<StretchLayout, StyleModifier>,
) {
// only trigger event if the event was not triggered already by a child
if will_bubble.insert(node.id) {
@@ -546,7 +544,7 @@ impl RinkInputHandler {
pub fn get_events<'a>(
&self,
layout: &Stretch,
tree: &mut ClientTree<StretchLayout, StyleModifier>,
tree: &mut RealDom<StretchLayout, StyleModifier>,
) -> Vec<UserEvent> {
let mut resolved_events = Vec::new();
+83
View File
@@ -0,0 +1,83 @@
use dioxus_core::*;
use dioxus_native_core::layout_attributes::apply_layout_attributes;
use dioxus_native_core::real_dom::BubbledUpState;
use stretch2::prelude::*;
/// the size
#[derive(Clone, PartialEq, Default, Debug)]
pub struct StretchLayout {
pub style: Style,
pub node: Option<Node>,
}
impl BubbledUpState for StretchLayout {
type Ctx = Stretch;
/// Setup the layout
fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, stretch: &mut Self::Ctx)
where
I: Iterator<Item = &'a Self>,
Self: 'a,
{
match vnode {
VNode::Text(t) => {
let char_len = t.text.chars().count();
let style = Style {
size: Size {
// characters are 1 point tall
height: Dimension::Points(1.0),
// text is as long as it is declared
width: Dimension::Points(char_len as f32),
},
..Default::default()
};
if let Some(n) = self.node {
if self.style != style {
stretch.set_style(n, style).unwrap();
}
} else {
self.node = Some(stretch.new_node(style, &[]).unwrap());
}
self.style = style;
}
VNode::Element(el) => {
// gather up all the styles from the attribute list
let mut style = Style::default();
for &Attribute { name, value, .. } in el.attributes {
apply_layout_attributes(name, value, &mut style);
}
// the root node fills the entire area
if el.id.get() == Some(ElementId(0)) {
apply_layout_attributes("width", "100%", &mut style);
apply_layout_attributes("height", "100%", &mut style);
}
// Set all direct nodes as our children
let mut child_layout = vec![];
for l in children {
child_layout.push(l.node.unwrap());
}
if let Some(n) = self.node {
if &stretch.children(n).unwrap() != &child_layout {
stretch.set_children(n, &child_layout).unwrap();
}
if self.style != style {
stretch.set_style(n, style).unwrap();
}
} else {
self.node = Some(stretch.new_node(style, &child_layout).unwrap());
}
self.style = style;
}
_ => (),
}
}
}
+51 -49
View File
@@ -9,16 +9,20 @@ use crossterm::{
};
use dioxus_core::exports::futures_channel::mpsc::unbounded;
use dioxus_core::*;
use dioxus_native_core::{client_tree::ClientTree, layout::StretchLayout};
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt};
use dioxus_native_core::real_dom::RealDom;
use futures::{
channel::mpsc::{UnboundedReceiver, UnboundedSender},
pin_mut, StreamExt,
};
use layout::StretchLayout;
use std::{io, time::Duration};
use stretch2::{prelude::Size, Stretch};
use style_attributes::StyleModifier;
use tokio::time::Instant;
use tui::{backend::CrosstermBackend, Terminal};
mod config;
mod hooks;
mod layout;
mod render;
mod style;
mod style_attributes;
@@ -28,6 +32,16 @@ pub use config::*;
pub use hooks::*;
pub use render::*;
#[derive(Clone)]
pub struct TuiContext {
tx: UnboundedSender<InputEvent>,
}
impl TuiContext {
pub fn quit(&self) {
self.tx.unbounded_send(InputEvent::Close).unwrap();
}
}
pub fn launch(app: Component<()>) {
launch_cfg(app, Config::default())
}
@@ -40,9 +54,26 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
let (handler, state) = RinkInputHandler::new(rx, cx);
cx.provide_root_context(state);
// Setup input handling
let (event_tx, event_rx) = unbounded();
let event_tx_clone = event_tx.clone();
std::thread::spawn(move || {
let tick_rate = Duration::from_millis(1000);
loop {
if crossterm::event::poll(tick_rate).unwrap() {
// if crossterm::event::poll(timeout).unwrap() {
let evt = crossterm::event::read().unwrap();
if event_tx.unbounded_send(InputEvent::UserInput(evt)).is_err() {
break;
}
}
}
});
let mut tree: ClientTree<StretchLayout, StyleModifier> = ClientTree::new();
cx.provide_root_context(state);
cx.provide_root_context(TuiContext { tx: event_tx_clone });
let mut tree: RealDom<StretchLayout, StyleModifier> = RealDom::new();
let mutations = dom.rebuild();
let to_update = tree.apply_mutations(vec![mutations]);
let mut stretch = Stretch::new();
@@ -50,47 +81,22 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
.update_state(&dom, to_update, &mut stretch, &mut ())
.unwrap();
render_vdom(&mut dom, tx, handler, cfg, tree, stretch).unwrap();
render_vdom(&mut dom, tx, event_rx, handler, cfg, tree, stretch).unwrap();
}
pub fn render_vdom(
fn render_vdom(
vdom: &mut VirtualDom,
ctx: UnboundedSender<TermEvent>,
crossterm_event_sender: UnboundedSender<TermEvent>,
mut event_reciever: UnboundedReceiver<InputEvent>,
handler: RinkInputHandler,
cfg: Config,
mut tree: ClientTree<StretchLayout, StyleModifier>,
mut tree: RealDom<StretchLayout, StyleModifier>,
mut stretch: Stretch,
) -> Result<()> {
// Setup input handling
let (tx, mut rx) = unbounded();
std::thread::spawn(move || {
let tick_rate = Duration::from_millis(100);
let mut last_tick = Instant::now();
loop {
// poll for tick rate duration, if no events, sent tick event.
let timeout = tick_rate
.checked_sub(last_tick.elapsed())
.unwrap_or_else(|| Duration::from_secs(0));
if crossterm::event::poll(timeout).unwrap() {
let evt = crossterm::event::read().unwrap();
tx.unbounded_send(InputEvent::UserInput(evt)).unwrap();
}
if last_tick.elapsed() >= tick_rate {
tx.unbounded_send(InputEvent::Tick).unwrap();
last_tick = Instant::now();
}
}
});
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async {
/*
Get the terminal to calcualte the layout from
*/
enable_raw_mode().unwrap();
let mut stdout = std::io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
@@ -103,10 +109,9 @@ pub fn render_vdom(
loop {
/*
-> collect all the nodes
-> resolve events
-> render the nodes in the right place with tui/crossterm
-> rendering
-> wait for changes
-> resolve events
-> lazily update the layout and style based on nodes changed
use simd to compare lines for diffing?
@@ -119,11 +124,11 @@ pub fn render_vdom(
terminal.draw(|frame| {
// size is guaranteed to not change when rendering
let dims = frame.size();
// println!("{dims:?}");
let width = dims.width;
let height = dims.height;
let root_id = tree.root_id();
let root_node = tree[root_id].up_state.node.unwrap();
stretch
.compute_layout(
root_node,
@@ -138,18 +143,12 @@ pub fn render_vdom(
})?;
}
// resolve events before rendering
// todo: events do not trigger update?
for e in handler.get_events(&stretch, &mut tree) {
vdom.handle_message(SchedulerMsg::Event(e));
}
use futures::future::{select, Either};
{
let wait = vdom.wait_for_work();
pin_mut!(wait);
match select(wait, rx.next()).await {
match select(wait, event_reciever.next()).await {
Either::Left((_a, _b)) => {
//
}
@@ -166,17 +165,21 @@ pub fn render_vdom(
TermEvent::Resize(_, _) => redraw = true,
TermEvent::Mouse(_) => {}
},
InputEvent::Tick => {} // tick
InputEvent::Close => break,
};
if let InputEvent::UserInput(evt) = evt.unwrap() {
ctx.unbounded_send(evt).unwrap();
crossterm_event_sender.unbounded_send(evt).unwrap();
}
}
}
}
// resolve events before rendering
for e in handler.get_events(&stretch, &mut tree) {
vdom.handle_message(SchedulerMsg::Event(e));
}
vdom.process_all_messages();
let mutations = vdom.work_with_deadline(|| false);
// updates the tree's nodes
let to_update = tree.apply_mutations(mutations);
@@ -200,7 +203,6 @@ pub fn render_vdom(
enum InputEvent {
UserInput(TermEvent),
Tick,
#[allow(dead_code)]
Close,
}
+4 -5
View File
@@ -1,7 +1,7 @@
use crate::layout::StretchLayout;
use dioxus_native_core::{
client_tree::{ClientTree, TreeNode},
layout::StretchLayout,
layout_attributes::UnitSystem,
real_dom::{RealDom, TreeNode},
};
use std::io::Stdout;
use stretch2::{
@@ -23,11 +23,11 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
pub fn render_vnode<'a>(
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
layout: &Stretch,
tree: &ClientTree<StretchLayout, StyleModifier>,
tree: &RealDom<StretchLayout, StyleModifier>,
node: &TreeNode<StretchLayout, StyleModifier>,
cfg: Config,
) {
use dioxus_native_core::client_tree::TreeNodeType;
use dioxus_native_core::real_dom::TreeNodeType;
match &node.node_type {
TreeNodeType::Placeholder => return,
@@ -51,7 +51,6 @@ pub fn render_vnode<'a>(
fn render(self, area: Rect, mut buf: RinkBuffer) {
for (i, c) in self.text.char_indices() {
let mut new_cell = RinkCell::default();
// println!("{:?}", self.style);
new_cell.set_style(self.style);
new_cell.symbol = c.to_string();
buf.set(area.left() + i as u16, area.top(), new_cell);
+1 -1
View File
@@ -28,7 +28,7 @@ impl RinkColor {
} else {
let [sr, sg, sb] = to_rgb(self.color).map(|e| e as u16);
let [or, og, ob] = to_rgb(other).map(|e| e as u16);
let sa: u16 = self.alpha as u16;
let sa = self.alpha as u16;
let rsa = 255 - sa;
Color::Rgb(
((sr * sa + or * rsa) / 255) as u8,
+1 -1
View File
@@ -31,8 +31,8 @@
use dioxus_core::{Attribute, VNode};
use dioxus_native_core::{
client_tree::PushedDownState,
layout_attributes::{parse_value, UnitSystem},
real_dom::PushedDownState,
};
use crate::style::{RinkColor, RinkStyle};