mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
bugfixes, docs, and pass clippy
This commit is contained in:
parent
5b25500c0b
commit
32b2e3a135
11 changed files with 350 additions and 306 deletions
|
@ -9,13 +9,12 @@ TUI support is currently quite experimental. Even the project name will change.
|
||||||
## Getting Set up
|
## Getting Set up
|
||||||
|
|
||||||
|
|
||||||
To tinker with TUI support, start by making a new package and adding our TUI package from git.
|
To tinker with TUI support, start by making a new package and adding our TUI feature.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cargo new --bin demo
|
$ cargo new --bin demo
|
||||||
$ cd demo
|
$ cd demo
|
||||||
$ cargo add dioxus
|
$ cargo add dioxus --features tui
|
||||||
$ cargo add rink --git https://github.com/DioxusLabs/rink.git
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,10 +26,7 @@ Then, edit your `main.rs` with the basic template.
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut dom = VirtualDom::new(app);
|
dioxus::tui::launch(app);
|
||||||
dom.rebuild();
|
|
||||||
|
|
||||||
rink::render_vdom(&mut dom).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
|
@ -54,7 +50,44 @@ To run our app:
|
||||||
$ cargo run
|
$ cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
Press "q" to close the app (yes, this is hardcoded, we are working on handlers to expose this in your code.)
|
Press "ctrl-c" to close the app. To switch from "ctrl-c" to just "q" to quit you can launch the app with a Configeration to disable the default quit and use the root TuiContext to quit on your own.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// main
|
||||||
|
use dioxus::events::{KeyCode, KeyboardEvent};
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus::tui::TuiContext;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dioxus::tui::launch_cfg(
|
||||||
|
app,
|
||||||
|
dioxus::tui::Config {
|
||||||
|
ctrl_c_quit: false,
|
||||||
|
// Some older terminals only support 16 colors or ANSI colors if your terminal is one of these change this to BaseColors or ANSI
|
||||||
|
rendering_mode: dioxus::tui::RenderingMode::Rgb,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app(cx: Scope) -> Element {
|
||||||
|
let tui_ctx: TuiContext = cx.consume_context().unwrap();
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
width: "100%",
|
||||||
|
height: "10px",
|
||||||
|
background_color: "red",
|
||||||
|
justify_content: "center",
|
||||||
|
align_items: "center",
|
||||||
|
onkeydown: move |k: KeyboardEvent| if let KeyCode::Q = k.data.key_code {
|
||||||
|
tui_ctx.quit();
|
||||||
|
},
|
||||||
|
|
||||||
|
"Hello world!"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ fn main() {
|
||||||
app,
|
app,
|
||||||
dioxus::tui::Config {
|
dioxus::tui::Config {
|
||||||
rendering_mode: dioxus::tui::RenderingMode::Ansi,
|
rendering_mode: dioxus::tui::RenderingMode::Ansi,
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,11 +255,11 @@ pub enum UnitSystem {
|
||||||
Point(f32),
|
Point(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Dimension> for UnitSystem {
|
impl From<UnitSystem> for Dimension {
|
||||||
fn into(self) -> Dimension {
|
fn from(other: UnitSystem) -> Dimension {
|
||||||
match self {
|
match other {
|
||||||
Self::Percent(v) => Dimension::Percent(v),
|
UnitSystem::Percent(v) => Dimension::Percent(v),
|
||||||
Self::Point(v) => Dimension::Points(v),
|
UnitSystem::Point(v) => Dimension::Points(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,25 +12,30 @@ use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RealDom<US: BubbledUpState = (), DS: PushedDownState = ()> {
|
pub struct RealDom<US: BubbledUpState = (), DS: PushedDownState = ()> {
|
||||||
root: usize,
|
root: usize,
|
||||||
nodes: Vec<Option<TreeNode<US, DS>>>,
|
nodes: Vec<Option<Node<US, DS>>>,
|
||||||
nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>,
|
nodes_listening: FxHashMap<&'static str, FxHashSet<usize>>,
|
||||||
node_stack: smallvec::SmallVec<[usize; 10]>,
|
node_stack: smallvec::SmallVec<[usize; 10]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<US: BubbledUpState, DS: PushedDownState> Default for RealDom<US, DS> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
pub fn new() -> RealDom<US, DS> {
|
pub fn new() -> RealDom<US, DS> {
|
||||||
RealDom {
|
RealDom {
|
||||||
root: 0,
|
root: 0,
|
||||||
nodes: {
|
nodes: {
|
||||||
let mut v = Vec::new();
|
let v = vec![Some(Node::new(
|
||||||
v.push(Some(TreeNode::new(
|
|
||||||
0,
|
0,
|
||||||
TreeNodeType::Element {
|
NodeType::Element {
|
||||||
tag: "Root".to_string(),
|
tag: "Root".to_string(),
|
||||||
namespace: Some("Root"),
|
namespace: Some("Root"),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
},
|
},
|
||||||
)));
|
))];
|
||||||
v
|
v
|
||||||
},
|
},
|
||||||
nodes_listening: FxHashMap::default(),
|
nodes_listening: FxHashMap::default(),
|
||||||
|
@ -47,7 +52,7 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
match e {
|
match e {
|
||||||
PushRoot { root } => self.node_stack.push(root as usize),
|
PushRoot { root } => self.node_stack.push(root as usize),
|
||||||
AppendChildren { many } => {
|
AppendChildren { many } => {
|
||||||
let target = if self.node_stack.len() >= many as usize + 1 {
|
let target = if self.node_stack.len() > many as usize {
|
||||||
*self
|
*self
|
||||||
.node_stack
|
.node_stack
|
||||||
.get(self.node_stack.len() - (many as usize + 1))
|
.get(self.node_stack.len() - (many as usize + 1))
|
||||||
|
@ -96,9 +101,9 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
self.remove(root as usize).unwrap();
|
self.remove(root as usize).unwrap();
|
||||||
}
|
}
|
||||||
CreateTextNode { root, text } => {
|
CreateTextNode { root, text } => {
|
||||||
let n = TreeNode::new(
|
let n = Node::new(
|
||||||
root,
|
root,
|
||||||
TreeNodeType::Text {
|
NodeType::Text {
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -106,9 +111,9 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
self.node_stack.push(root as usize)
|
self.node_stack.push(root as usize)
|
||||||
}
|
}
|
||||||
CreateElement { root, tag } => {
|
CreateElement { root, tag } => {
|
||||||
let n = TreeNode::new(
|
let n = Node::new(
|
||||||
root,
|
root,
|
||||||
TreeNodeType::Element {
|
NodeType::Element {
|
||||||
tag: tag.to_string(),
|
tag: tag.to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
@ -118,9 +123,9 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
self.node_stack.push(root as usize)
|
self.node_stack.push(root as usize)
|
||||||
}
|
}
|
||||||
CreateElementNs { root, tag, ns } => {
|
CreateElementNs { root, tag, ns } => {
|
||||||
let n = TreeNode::new(
|
let n = Node::new(
|
||||||
root,
|
root,
|
||||||
TreeNodeType::Element {
|
NodeType::Element {
|
||||||
tag: tag.to_string(),
|
tag: tag.to_string(),
|
||||||
namespace: Some(ns),
|
namespace: Some(ns),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
@ -130,7 +135,7 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
self.node_stack.push(root as usize)
|
self.node_stack.push(root as usize)
|
||||||
}
|
}
|
||||||
CreatePlaceholder { root } => {
|
CreatePlaceholder { root } => {
|
||||||
let n = TreeNode::new(root, TreeNodeType::Placeholder);
|
let n = Node::new(root, NodeType::Placeholder);
|
||||||
self.insert(n);
|
self.insert(n);
|
||||||
self.node_stack.push(root as usize)
|
self.node_stack.push(root as usize)
|
||||||
}
|
}
|
||||||
|
@ -159,7 +164,7 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
let target = &mut self[root as usize];
|
let target = &mut self[root as usize];
|
||||||
nodes_updated.push(root as usize);
|
nodes_updated.push(root as usize);
|
||||||
match &mut target.node_type {
|
match &mut target.node_type {
|
||||||
TreeNodeType::Text { text } => {
|
NodeType::Text { text } => {
|
||||||
*text = new_text.to_string();
|
*text = new_text.to_string();
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -204,13 +209,13 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
let node_type = &node.node_type;
|
let node_type = &node.node_type;
|
||||||
let up_state = &mut node.up_state;
|
let up_state = &mut node.up_state;
|
||||||
let children = match node_type {
|
let children = match node_type {
|
||||||
TreeNodeType::Element { children, .. } => Some(children),
|
NodeType::Element { children, .. } => Some(children),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
// todo: reduce cloning state
|
// todo: reduce cloning state
|
||||||
let old = up_state.clone();
|
let old = up_state.clone();
|
||||||
let mut new = up_state.clone();
|
let mut new = up_state.clone();
|
||||||
let parent = node.parent.clone();
|
let parent = node.parent;
|
||||||
new.reduce(
|
new.reduce(
|
||||||
children
|
children
|
||||||
.unwrap_or(&Vec::new())
|
.unwrap_or(&Vec::new())
|
||||||
|
@ -254,19 +259,16 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
if new != old {
|
if new != old {
|
||||||
to_rerender.insert(id);
|
to_rerender.insert(id);
|
||||||
let node = &mut self[id as usize];
|
let node = &mut self[id as usize];
|
||||||
match &node.node_type {
|
if let NodeType::Element { children, .. } = &node.node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
for c in children {
|
||||||
for c in children {
|
let i = to_push.partition_point(|(other_id, h)| {
|
||||||
let i = to_push.partition_point(|(other_id, h)| {
|
*h < height + 1 || (*h == height + 1 && *other_id < c.0)
|
||||||
*h < height + 1 || (*h == height + 1 && *other_id < c.0)
|
});
|
||||||
});
|
if i >= to_push.len() || to_push[i].0 != c.0 {
|
||||||
if i >= to_push.len() || to_push[i].0 != c.0 {
|
to_push.insert(i, (c.0, height + 1));
|
||||||
to_push.insert(i, (c.0, height + 1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
}
|
||||||
};
|
|
||||||
node.down_state = new;
|
node.down_state = new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,31 +289,25 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
fn increase_height(&mut self, id: usize, amount: u16) {
|
fn increase_height(&mut self, id: usize, amount: u16) {
|
||||||
let n = &mut self[id];
|
let n = &mut self[id];
|
||||||
n.height += amount;
|
n.height += amount;
|
||||||
match &n.node_type {
|
if let NodeType::Element { children, .. } = &n.node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
for c in children.clone() {
|
||||||
for c in children.clone() {
|
self.increase_height(c.0, amount);
|
||||||
self.increase_height(c.0, amount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove a node and it's children from the tree.
|
// remove a node and it's children from the tree.
|
||||||
fn remove(&mut self, id: usize) -> Option<TreeNode<US, DS>> {
|
fn remove(&mut self, id: usize) -> Option<Node<US, DS>> {
|
||||||
// We do not need to remove the node from the parent's children list for children.
|
// We do not need to remove the node from the parent's children list for children.
|
||||||
fn inner<US: BubbledUpState, DS: PushedDownState>(
|
fn inner<US: BubbledUpState, DS: PushedDownState>(
|
||||||
tree: &mut RealDom<US, DS>,
|
tree: &mut RealDom<US, DS>,
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Option<TreeNode<US, DS>> {
|
) -> Option<Node<US, DS>> {
|
||||||
let mut node = tree.nodes[id as usize].take()?;
|
let mut node = tree.nodes[id as usize].take()?;
|
||||||
match &mut node.node_type {
|
if let NodeType::Element { children, .. } = &mut node.node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
for c in children {
|
||||||
for c in children {
|
inner(tree, c.0)?;
|
||||||
inner(tree, c.0)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
Some(node)
|
Some(node)
|
||||||
}
|
}
|
||||||
|
@ -320,18 +316,15 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
let parent = &mut self[parent];
|
let parent = &mut self[parent];
|
||||||
parent.remove_child(ElementId(id));
|
parent.remove_child(ElementId(id));
|
||||||
}
|
}
|
||||||
match &mut node.node_type {
|
if let NodeType::Element { children, .. } = &mut node.node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
for c in children {
|
||||||
for c in children {
|
inner(self, c.0)?;
|
||||||
inner(self, c.0)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
Some(node)
|
Some(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, node: TreeNode<US, DS>) {
|
fn insert(&mut self, node: Node<US, DS>) {
|
||||||
let current_len = self.nodes.len();
|
let current_len = self.nodes.len();
|
||||||
let id = node.id.0;
|
let id = node.id.0;
|
||||||
if current_len - 1 < node.id.0 {
|
if current_len - 1 < node.id.0 {
|
||||||
|
@ -341,15 +334,15 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
self.nodes[id] = Some(node);
|
self.nodes[id] = Some(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, id: usize) -> Option<&TreeNode<US, DS>> {
|
pub fn get(&self, id: usize) -> Option<&Node<US, DS>> {
|
||||||
self.nodes.get(id)?.as_ref()
|
self.nodes.get(id)?.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, id: usize) -> Option<&mut TreeNode<US, DS>> {
|
pub fn get_mut(&mut self, id: usize) -> Option<&mut Node<US, DS>> {
|
||||||
self.nodes.get_mut(id)?.as_mut()
|
self.nodes.get_mut(id)?.as_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&TreeNode<US, DS>> {
|
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node<US, DS>> {
|
||||||
if let Some(nodes) = self.nodes_listening.get(event) {
|
if let Some(nodes) = self.nodes_listening.get(event) {
|
||||||
let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
|
let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect();
|
||||||
listening.sort_by(|n1, n2| (n1.height).cmp(&n2.height).reverse());
|
listening.sort_by(|n1, n2| (n1.height).cmp(&n2.height).reverse());
|
||||||
|
@ -369,7 +362,7 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
if let Some(id) = e.id.get() {
|
if let Some(id) = e.id.get() {
|
||||||
let tree_node = &self[id];
|
let tree_node = &self[id];
|
||||||
match &tree_node.node_type {
|
match &tree_node.node_type {
|
||||||
TreeNodeType::Element {
|
NodeType::Element {
|
||||||
tag,
|
tag,
|
||||||
namespace,
|
namespace,
|
||||||
children,
|
children,
|
||||||
|
@ -398,7 +391,7 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
if let Some(id) = t.id.get() {
|
if let Some(id) = t.id.get() {
|
||||||
let tree_node = &self[id];
|
let tree_node = &self[id];
|
||||||
match &tree_node.node_type {
|
match &tree_node.node_type {
|
||||||
TreeNodeType::Text { text } => t.text == text,
|
NodeType::Text { text } => t.text == text,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -420,36 +413,53 @@ impl<US: BubbledUpState, DS: PushedDownState> RealDom<US, DS> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function for each node in the tree, depth first.
|
/// Call a function for each node in the tree, depth first.
|
||||||
pub fn traverse_depth_first(&self, mut f: impl FnMut(&TreeNode<US, DS>)) {
|
pub fn traverse_depth_first(&self, mut f: impl FnMut(&Node<US, DS>)) {
|
||||||
fn inner<US: BubbledUpState, DS: PushedDownState>(
|
fn inner<US: BubbledUpState, DS: PushedDownState>(
|
||||||
tree: &RealDom<US, DS>,
|
tree: &RealDom<US, DS>,
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
f: &mut impl FnMut(&TreeNode<US, DS>),
|
f: &mut impl FnMut(&Node<US, DS>),
|
||||||
) {
|
) {
|
||||||
let node = &tree[id];
|
let node = &tree[id];
|
||||||
f(node);
|
f(node);
|
||||||
match &node.node_type {
|
if let NodeType::Element { children, .. } = &node.node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
for c in children {
|
||||||
for c in children {
|
inner(tree, *c, f);
|
||||||
inner(tree, *c, f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &self[self.root].node_type {
|
if let NodeType::Element { children, .. } = &self[self.root].node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
for c in children {
|
||||||
for c in children {
|
inner(self, *c, &mut f);
|
||||||
inner(self, *c, &mut f);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a function for each node in the tree, depth first.
|
||||||
|
pub fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(&mut Node<US, DS>)) {
|
||||||
|
fn inner<US: BubbledUpState, DS: PushedDownState>(
|
||||||
|
tree: &mut RealDom<US, DS>,
|
||||||
|
id: ElementId,
|
||||||
|
f: &mut impl FnMut(&mut Node<US, DS>),
|
||||||
|
) {
|
||||||
|
let node = &mut tree[id];
|
||||||
|
f(node);
|
||||||
|
if let NodeType::Element { children, .. } = &mut node.node_type {
|
||||||
|
for c in children.clone() {
|
||||||
|
inner(tree, c, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
}
|
||||||
|
let root = self.root;
|
||||||
|
if let NodeType::Element { children, .. } = &mut self[root].node_type {
|
||||||
|
for c in children.clone() {
|
||||||
|
inner(self, c, &mut f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<US: BubbledUpState, DS: PushedDownState> Index<usize> for RealDom<US, DS> {
|
impl<US: BubbledUpState, DS: PushedDownState> Index<usize> for RealDom<US, DS> {
|
||||||
type Output = TreeNode<US, DS>;
|
type Output = Node<US, DS>;
|
||||||
|
|
||||||
fn index(&self, idx: usize) -> &Self::Output {
|
fn index(&self, idx: usize) -> &Self::Output {
|
||||||
self.get(idx).expect("Node does not exist")
|
self.get(idx).expect("Node does not exist")
|
||||||
|
@ -457,7 +467,7 @@ impl<US: BubbledUpState, DS: PushedDownState> Index<usize> for RealDom<US, DS> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<US: BubbledUpState, DS: PushedDownState> Index<ElementId> for RealDom<US, DS> {
|
impl<US: BubbledUpState, DS: PushedDownState> Index<ElementId> for RealDom<US, DS> {
|
||||||
type Output = TreeNode<US, DS>;
|
type Output = Node<US, DS>;
|
||||||
|
|
||||||
fn index(&self, idx: ElementId) -> &Self::Output {
|
fn index(&self, idx: ElementId) -> &Self::Output {
|
||||||
&self[idx.0]
|
&self[idx.0]
|
||||||
|
@ -477,7 +487,7 @@ impl<US: BubbledUpState, DS: PushedDownState> IndexMut<ElementId> for RealDom<US
|
||||||
|
|
||||||
/// The node is stored client side and stores only basic data about the node. For more complete information about the node see [`TreeNode::element`].
|
/// The node is stored client side and stores only basic data about the node. For more complete information about the node see [`TreeNode::element`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TreeNode<US: BubbledUpState, DS: PushedDownState> {
|
pub struct Node<US: BubbledUpState, DS: PushedDownState> {
|
||||||
/// The id of the node this node was created from.
|
/// The id of the node this node was created from.
|
||||||
pub id: ElementId,
|
pub id: ElementId,
|
||||||
/// The parent id of the node.
|
/// The parent id of the node.
|
||||||
|
@ -487,13 +497,13 @@ pub struct TreeNode<US: BubbledUpState, DS: PushedDownState> {
|
||||||
/// State of the node that is pushed down to the children. The state must depend only on the node itself and its parent.
|
/// State of the node that is pushed down to the children. The state must depend only on the node itself and its parent.
|
||||||
pub down_state: DS,
|
pub down_state: DS,
|
||||||
/// Additional inforation specific to the node type
|
/// Additional inforation specific to the node type
|
||||||
pub node_type: TreeNodeType,
|
pub node_type: NodeType,
|
||||||
/// The number of parents before the root node. The root node has height 1.
|
/// The number of parents before the root node. The root node has height 1.
|
||||||
pub height: u16,
|
pub height: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TreeNodeType {
|
pub enum NodeType {
|
||||||
Text {
|
Text {
|
||||||
text: String,
|
text: String,
|
||||||
},
|
},
|
||||||
|
@ -505,9 +515,9 @@ pub enum TreeNodeType {
|
||||||
Placeholder,
|
Placeholder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<US: BubbledUpState, DS: PushedDownState> TreeNode<US, DS> {
|
impl<US: BubbledUpState, DS: PushedDownState> Node<US, DS> {
|
||||||
fn new(id: u64, node_type: TreeNodeType) -> Self {
|
fn new(id: u64, node_type: NodeType) -> Self {
|
||||||
TreeNode {
|
Node {
|
||||||
id: ElementId(id as usize),
|
id: ElementId(id as usize),
|
||||||
parent: None,
|
parent: None,
|
||||||
node_type,
|
node_type,
|
||||||
|
@ -523,20 +533,14 @@ impl<US: BubbledUpState, DS: PushedDownState> TreeNode<US, DS> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_child(&mut self, child: ElementId) {
|
fn add_child(&mut self, child: ElementId) {
|
||||||
match &mut self.node_type {
|
if let NodeType::Element { children, .. } = &mut self.node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
children.push(child);
|
||||||
children.push(child);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_child(&mut self, child: ElementId) {
|
fn remove_child(&mut self, child: ElementId) {
|
||||||
match &mut self.node_type {
|
if let NodeType::Element { children, .. } = &mut self.node_type {
|
||||||
TreeNodeType::Element { children, .. } => {
|
children.retain(|c| c != &child);
|
||||||
children.retain(|c| c != &child);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
#[derive(Default, Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub rendering_mode: RenderingMode,
|
pub rendering_mode: RenderingMode,
|
||||||
|
/// Should the terminal quit when the user presses `ctrl+c`?
|
||||||
|
/// To handle quiting on your own, use the [crate::TuiContext] root context.
|
||||||
|
pub ctrl_c_quit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
rendering_mode: Default::default(),
|
||||||
|
ctrl_c_quit: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
|
@ -5,8 +5,7 @@ use dioxus_core::*;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use dioxus_html::{on::*, KeyCode};
|
use dioxus_html::{on::*, KeyCode};
|
||||||
use dioxus_native_core::real_dom::{RealDom, TreeNode};
|
use dioxus_native_core::real_dom::{Node, RealDom};
|
||||||
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
|
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
@ -162,7 +161,7 @@ impl InnerInputState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update<'a>(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
evts: &mut Vec<EventCore>,
|
evts: &mut Vec<EventCore>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
|
@ -176,9 +175,11 @@ impl InnerInputState {
|
||||||
|
|
||||||
self.wheel = None;
|
self.wheel = None;
|
||||||
|
|
||||||
|
println!("update {evts:?}");
|
||||||
for e in evts.iter_mut() {
|
for e in evts.iter_mut() {
|
||||||
self.apply_event(e);
|
self.apply_event(e);
|
||||||
}
|
}
|
||||||
|
println!("->update {evts:?}");
|
||||||
|
|
||||||
self.resolve_mouse_events(previous_mouse, resolved_events, layout, tree);
|
self.resolve_mouse_events(previous_mouse, resolved_events, layout, tree);
|
||||||
|
|
||||||
|
@ -216,7 +217,7 @@ impl InnerInputState {
|
||||||
data: Arc<dyn Any + Send + Sync>,
|
data: Arc<dyn Any + Send + Sync>,
|
||||||
will_bubble: &mut FxHashSet<ElementId>,
|
will_bubble: &mut FxHashSet<ElementId>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
node: &TreeNode<StretchLayout, StyleModifier>,
|
node: &Node<StretchLayout, StyleModifier>,
|
||||||
tree: &RealDom<StretchLayout, StyleModifier>,
|
tree: &RealDom<StretchLayout, StyleModifier>,
|
||||||
) {
|
) {
|
||||||
// only trigger event if the event was not triggered already by a child
|
// only trigger event if the event was not triggered already by a child
|
||||||
|
@ -231,7 +232,7 @@ impl InnerInputState {
|
||||||
priority: EventPriority::Medium,
|
priority: EventPriority::Medium,
|
||||||
name,
|
name,
|
||||||
element: Some(node.id),
|
element: Some(node.id),
|
||||||
data: data,
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,17 +270,15 @@ impl InnerInputState {
|
||||||
.is_some();
|
.is_some();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if currently_contains && previously_contained {
|
||||||
if previously_contained {
|
try_create_event(
|
||||||
try_create_event(
|
"mousemove",
|
||||||
"mousemove",
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
Arc::new(clone_mouse_data(data.mouse_data)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,17 +294,15 @@ impl InnerInputState {
|
||||||
.is_some();
|
.is_some();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if currently_contains && !previously_contained {
|
||||||
if !previously_contained {
|
try_create_event(
|
||||||
try_create_event(
|
"mouseenter",
|
||||||
"mouseenter",
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
Arc::new(clone_mouse_data(data.mouse_data)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,17 +318,15 @@ impl InnerInputState {
|
||||||
.is_some();
|
.is_some();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if currently_contains && !previously_contained {
|
||||||
if !previously_contained {
|
try_create_event(
|
||||||
try_create_event(
|
"mouseover",
|
||||||
"mouseover",
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
Arc::new(clone_mouse_data(data.mouse_data)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -343,17 +338,15 @@ impl InnerInputState {
|
||||||
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if currently_contains && data.clicked {
|
||||||
if data.clicked {
|
try_create_event(
|
||||||
try_create_event(
|
"mousedown",
|
||||||
"mousedown",
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
Arc::new(clone_mouse_data(data.mouse_data)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,17 +358,15 @@ impl InnerInputState {
|
||||||
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if currently_contains && data.released {
|
||||||
if data.released {
|
try_create_event(
|
||||||
try_create_event(
|
"mouseup",
|
||||||
"mouseup",
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
Arc::new(clone_mouse_data(data.mouse_data)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -387,17 +378,15 @@ impl InnerInputState {
|
||||||
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if currently_contains && data.released && data.mouse_data.button == 0 {
|
||||||
if data.released && data.mouse_data.button == 0 {
|
try_create_event(
|
||||||
try_create_event(
|
"click",
|
||||||
"click",
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
Arc::new(clone_mouse_data(data.mouse_data)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,17 +398,15 @@ impl InnerInputState {
|
||||||
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if currently_contains && data.released && data.mouse_data.button == 2 {
|
||||||
if data.released && data.mouse_data.button == 2 {
|
try_create_event(
|
||||||
try_create_event(
|
"contextmenu",
|
||||||
"contextmenu",
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
Arc::new(clone_mouse_data(data.mouse_data)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,18 +418,16 @@ impl InnerInputState {
|
||||||
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
if currently_contains {
|
if let Some(w) = data.wheel_data {
|
||||||
if let Some(w) = data.wheel_data {
|
if currently_contains && data.wheel_delta != 0.0 {
|
||||||
if data.wheel_delta != 0.0 {
|
try_create_event(
|
||||||
try_create_event(
|
"wheel",
|
||||||
"wheel",
|
Arc::new(clone_wheel_data(w)),
|
||||||
Arc::new(clone_wheel_data(w)),
|
&mut will_bubble,
|
||||||
&mut will_bubble,
|
resolved_events,
|
||||||
resolved_events,
|
node,
|
||||||
node,
|
tree,
|
||||||
tree,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,24 +496,22 @@ pub struct RinkInputHandler {
|
||||||
impl RinkInputHandler {
|
impl RinkInputHandler {
|
||||||
/// global context that handles events
|
/// global context that handles events
|
||||||
/// limitations: GUI key modifier is never detected, key up events are not detected, and only two mouse buttons may be pressed at once
|
/// limitations: GUI key modifier is never detected, key up events are not detected, and only two mouse buttons may be pressed at once
|
||||||
pub fn new(
|
pub fn new() -> (
|
||||||
mut receiver: UnboundedReceiver<TermEvent>,
|
Self,
|
||||||
cx: &ScopeState,
|
Rc<RefCell<InnerInputState>>,
|
||||||
) -> (Self, Rc<RefCell<InnerInputState>>) {
|
impl FnMut(crossterm::event::Event),
|
||||||
|
) {
|
||||||
let queued_events = Rc::new(RefCell::new(Vec::new()));
|
let queued_events = Rc::new(RefCell::new(Vec::new()));
|
||||||
let queued_events2 = Rc::downgrade(&queued_events);
|
let queued_events2 = Rc::downgrade(&queued_events);
|
||||||
|
|
||||||
cx.push_future(async move {
|
let regester_event = move |evt: crossterm::event::Event| {
|
||||||
while let Some(evt) = receiver.next().await {
|
if let Some(evt) = get_event(evt) {
|
||||||
if let Some(evt) = get_event(evt) {
|
if let Some(v) = queued_events2.upgrade() {
|
||||||
if let Some(v) = queued_events2.upgrade() {
|
println!("queued event: {:?}", evt);
|
||||||
(*v).borrow_mut().push(evt);
|
(*v).borrow_mut().push(evt);
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
let state = Rc::new(RefCell::new(InnerInputState::new()));
|
let state = Rc::new(RefCell::new(InnerInputState::new()));
|
||||||
|
|
||||||
|
@ -538,14 +521,16 @@ impl RinkInputHandler {
|
||||||
queued_events,
|
queued_events,
|
||||||
},
|
},
|
||||||
state,
|
state,
|
||||||
|
regester_event,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_events<'a>(
|
pub fn get_events(
|
||||||
&self,
|
&self,
|
||||||
layout: &Stretch,
|
layout: &Stretch,
|
||||||
tree: &mut RealDom<StretchLayout, StyleModifier>,
|
tree: &mut RealDom<StretchLayout, StyleModifier>,
|
||||||
) -> Vec<UserEvent> {
|
) -> Vec<UserEvent> {
|
||||||
|
println!("get_events");
|
||||||
let mut resolved_events = Vec::new();
|
let mut resolved_events = Vec::new();
|
||||||
|
|
||||||
(*self.state).borrow_mut().update(
|
(*self.state).borrow_mut().update(
|
||||||
|
@ -799,7 +784,7 @@ fn translate_key_event(event: crossterm::event::KeyEvent) -> Option<EventData> {
|
||||||
// from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
|
// from https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
|
||||||
Some(EventData::Keyboard(KeyboardData {
|
Some(EventData::Keyboard(KeyboardData {
|
||||||
char_code: code.raw_code(),
|
char_code: code.raw_code(),
|
||||||
key: key_str.to_string(),
|
key: key_str,
|
||||||
key_code: code,
|
key_code: code,
|
||||||
alt_key: event.modifiers.contains(KeyModifiers::ALT),
|
alt_key: event.modifiers.contains(KeyModifiers::ALT),
|
||||||
ctrl_key: event.modifiers.contains(KeyModifiers::CONTROL),
|
ctrl_key: event.modifiers.contains(KeyModifiers::CONTROL),
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl BubbledUpState for StretchLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(n) = self.node {
|
if let Some(n) = self.node {
|
||||||
if &stretch.children(n).unwrap() != &child_layout {
|
if stretch.children(n).unwrap() != child_layout {
|
||||||
stretch.set_children(n, &child_layout).unwrap();
|
stretch.set_children(n, &child_layout).unwrap();
|
||||||
}
|
}
|
||||||
if self.style != style {
|
if self.style != style {
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// notes:
|
|
||||||
// mouse events binary search was broken for absolutely positioned elements
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyModifiers},
|
event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyModifiers},
|
||||||
|
@ -48,11 +45,8 @@ pub fn launch(app: Component<()>) {
|
||||||
|
|
||||||
pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
let (tx, rx) = unbounded();
|
|
||||||
|
|
||||||
let cx = dom.base_scope();
|
let (handler, state, register_event) = RinkInputHandler::new();
|
||||||
|
|
||||||
let (handler, state) = RinkInputHandler::new(rx, cx);
|
|
||||||
|
|
||||||
// Setup input handling
|
// Setup input handling
|
||||||
let (event_tx, event_rx) = unbounded();
|
let (event_tx, event_rx) = unbounded();
|
||||||
|
@ -70,6 +64,7 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let cx = dom.base_scope();
|
||||||
cx.provide_root_context(state);
|
cx.provide_root_context(state);
|
||||||
cx.provide_root_context(TuiContext { tx: event_tx_clone });
|
cx.provide_root_context(TuiContext { tx: event_tx_clone });
|
||||||
|
|
||||||
|
@ -81,17 +76,26 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
|
||||||
.update_state(&dom, to_update, &mut stretch, &mut ())
|
.update_state(&dom, to_update, &mut stretch, &mut ())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
render_vdom(&mut dom, tx, event_rx, handler, cfg, tree, stretch).unwrap();
|
render_vdom(
|
||||||
|
&mut dom,
|
||||||
|
event_rx,
|
||||||
|
handler,
|
||||||
|
cfg,
|
||||||
|
tree,
|
||||||
|
stretch,
|
||||||
|
register_event,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_vdom(
|
fn render_vdom(
|
||||||
vdom: &mut VirtualDom,
|
vdom: &mut VirtualDom,
|
||||||
crossterm_event_sender: UnboundedSender<TermEvent>,
|
|
||||||
mut event_reciever: UnboundedReceiver<InputEvent>,
|
mut event_reciever: UnboundedReceiver<InputEvent>,
|
||||||
handler: RinkInputHandler,
|
handler: RinkInputHandler,
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
mut tree: RealDom<StretchLayout, StyleModifier>,
|
mut tree: RealDom<StretchLayout, StyleModifier>,
|
||||||
mut stretch: Stretch,
|
mut stretch: Stretch,
|
||||||
|
mut register_event: impl FnMut(crossterm::event::Event),
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
|
@ -105,7 +109,7 @@ fn render_vdom(
|
||||||
|
|
||||||
terminal.clear().unwrap();
|
terminal.clear().unwrap();
|
||||||
let mut to_rerender: fxhash::FxHashSet<usize> = vec![0].into_iter().collect();
|
let mut to_rerender: fxhash::FxHashSet<usize> = vec![0].into_iter().collect();
|
||||||
let mut redraw = true;
|
let mut resized = true;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
/*
|
/*
|
||||||
|
@ -119,8 +123,8 @@ fn render_vdom(
|
||||||
todo: lazy re-rendering
|
todo: lazy re-rendering
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if !to_rerender.is_empty() || redraw {
|
if !to_rerender.is_empty() || resized {
|
||||||
redraw = false;
|
resized = false;
|
||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
// size is guaranteed to not change when rendering
|
// size is guaranteed to not change when rendering
|
||||||
let dims = frame.size();
|
let dims = frame.size();
|
||||||
|
@ -138,8 +142,8 @@ fn render_vdom(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let root = &tree[tree.root_id()];
|
// let root = &tree[tree.root_id()];
|
||||||
render::render_vnode(frame, &stretch, &tree, &root, cfg);
|
// render::render_vnode(frame, &stretch, &tree, &root, cfg);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,35 +162,44 @@ fn render_vdom(
|
||||||
TermEvent::Key(key) => {
|
TermEvent::Key(key) => {
|
||||||
if matches!(key.code, KeyCode::Char('C' | 'c'))
|
if matches!(key.code, KeyCode::Char('C' | 'c'))
|
||||||
&& key.modifiers.contains(KeyModifiers::CONTROL)
|
&& key.modifiers.contains(KeyModifiers::CONTROL)
|
||||||
|
&& cfg.ctrl_c_quit
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TermEvent::Resize(_, _) => redraw = true,
|
TermEvent::Resize(_, _) => resized = true,
|
||||||
TermEvent::Mouse(_) => {}
|
TermEvent::Mouse(_) => {}
|
||||||
},
|
},
|
||||||
InputEvent::Close => break,
|
InputEvent::Close => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let InputEvent::UserInput(evt) = evt.unwrap() {
|
if let InputEvent::UserInput(evt) = evt.unwrap() {
|
||||||
crossterm_event_sender.unbounded_send(evt).unwrap();
|
register_event(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve events before rendering
|
{
|
||||||
for e in handler.get_events(&stretch, &mut tree) {
|
// resolve events before rendering
|
||||||
vdom.handle_message(SchedulerMsg::Event(e));
|
let evts = handler.get_events(&stretch, &mut tree);
|
||||||
|
println!("evts: {:?}", evts);
|
||||||
|
for e in evts {
|
||||||
|
vdom.handle_message(SchedulerMsg::Event(e));
|
||||||
|
}
|
||||||
|
let mutations = vdom.work_with_deadline(|| false);
|
||||||
|
// updates the tree's nodes
|
||||||
|
let to_update = tree.apply_mutations(mutations);
|
||||||
|
// update the style and layout
|
||||||
|
to_rerender.extend(
|
||||||
|
tree.update_state(vdom, to_update, &mut stretch, &mut ())
|
||||||
|
.unwrap()
|
||||||
|
.iter(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
vdom.process_all_messages();
|
println!();
|
||||||
let mutations = vdom.work_with_deadline(|| false);
|
println!();
|
||||||
// updates the tree's nodes
|
println!();
|
||||||
let to_update = tree.apply_mutations(mutations);
|
|
||||||
// update the style and layout
|
|
||||||
to_rerender = tree
|
|
||||||
.update_state(&vdom, to_update, &mut stretch, &mut ())
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
|
@ -203,6 +216,5 @@ fn render_vdom(
|
||||||
|
|
||||||
enum InputEvent {
|
enum InputEvent {
|
||||||
UserInput(TermEvent),
|
UserInput(TermEvent),
|
||||||
#[allow(dead_code)]
|
|
||||||
Close,
|
Close,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::layout::StretchLayout;
|
use crate::layout::StretchLayout;
|
||||||
use dioxus_native_core::{
|
use dioxus_native_core::{
|
||||||
layout_attributes::UnitSystem,
|
layout_attributes::UnitSystem,
|
||||||
real_dom::{RealDom, TreeNode},
|
real_dom::{Node, RealDom},
|
||||||
};
|
};
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
use stretch2::{
|
use stretch2::{
|
||||||
|
@ -20,18 +20,17 @@ use crate::{
|
||||||
|
|
||||||
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
||||||
|
|
||||||
pub fn render_vnode<'a>(
|
pub fn render_vnode(
|
||||||
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
||||||
layout: &Stretch,
|
layout: &Stretch,
|
||||||
tree: &RealDom<StretchLayout, StyleModifier>,
|
tree: &RealDom<StretchLayout, StyleModifier>,
|
||||||
node: &TreeNode<StretchLayout, StyleModifier>,
|
node: &Node<StretchLayout, StyleModifier>,
|
||||||
cfg: Config,
|
cfg: Config,
|
||||||
) {
|
) {
|
||||||
use dioxus_native_core::real_dom::TreeNodeType;
|
use dioxus_native_core::real_dom::NodeType;
|
||||||
|
|
||||||
match &node.node_type {
|
if let NodeType::Placeholder = &node.node_type {
|
||||||
TreeNodeType::Placeholder => return,
|
return;
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let Layout { location, size, .. } = layout.layout(node.up_state.node.unwrap()).unwrap();
|
let Layout { location, size, .. } = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
@ -40,7 +39,7 @@ pub fn render_vnode<'a>(
|
||||||
let Size { width, height } = size;
|
let Size { width, height } = size;
|
||||||
|
|
||||||
match &node.node_type {
|
match &node.node_type {
|
||||||
TreeNodeType::Text { text } => {
|
NodeType::Text { text } => {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Label<'a> {
|
struct Label<'a> {
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
|
@ -59,7 +58,7 @@ pub fn render_vnode<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let label = Label {
|
let label = Label {
|
||||||
text: &text,
|
text,
|
||||||
style: node.down_state.style,
|
style: node.down_state.style,
|
||||||
};
|
};
|
||||||
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
|
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
|
||||||
|
@ -69,7 +68,7 @@ pub fn render_vnode<'a>(
|
||||||
frame.render_widget(WidgetWithContext::new(label, cfg), area);
|
frame.render_widget(WidgetWithContext::new(label, cfg), area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TreeNodeType::Element { children, .. } => {
|
NodeType::Element { children, .. } => {
|
||||||
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
|
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
|
||||||
|
|
||||||
// the renderer will panic if a node is rendered out of range even if the size is zero
|
// the renderer will panic if a node is rendered out of range even if the size is zero
|
||||||
|
@ -81,11 +80,11 @@ pub fn render_vnode<'a>(
|
||||||
render_vnode(frame, layout, tree, &tree[c.0], cfg);
|
render_vnode(frame, layout, tree, &tree[c.0], cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TreeNodeType::Placeholder => unreachable!(),
|
NodeType::Placeholder => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RinkWidget for &TreeNode<StretchLayout, StyleModifier> {
|
impl RinkWidget for &Node<StretchLayout, StyleModifier> {
|
||||||
fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
|
fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
|
||||||
use tui::symbols::line::*;
|
use tui::symbols::line::*;
|
||||||
|
|
||||||
|
@ -249,8 +248,8 @@ impl RinkWidget for &TreeNode<StretchLayout, StyleModifier> {
|
||||||
|
|
||||||
fn get_radius(border: &BorderEdge, area: Rect) -> f32 {
|
fn get_radius(border: &BorderEdge, area: Rect) -> f32 {
|
||||||
match border.style {
|
match border.style {
|
||||||
BorderStyle::HIDDEN => 0.0,
|
BorderStyle::Hidden => 0.0,
|
||||||
BorderStyle::NONE => 0.0,
|
BorderStyle::None => 0.0,
|
||||||
_ => match border.radius {
|
_ => match border.radius {
|
||||||
UnitSystem::Percent(p) => p * area.width as f32 / 100.0,
|
UnitSystem::Percent(p) => p * area.width as f32 / 100.0,
|
||||||
UnitSystem::Point(p) => p,
|
UnitSystem::Point(p) => p,
|
||||||
|
|
|
@ -51,33 +51,28 @@ impl PushedDownState for StyleModifier {
|
||||||
if parent.is_some() {
|
if parent.is_some() {
|
||||||
self.style.fg = None;
|
self.style.fg = None;
|
||||||
}
|
}
|
||||||
match vnode {
|
if let VNode::Element(el) = vnode {
|
||||||
VNode::Element(el) => {
|
// handle text modifier elements
|
||||||
// handle text modifier elements
|
if el.namespace.is_none() {
|
||||||
if el.namespace.is_none() {
|
match el.tag {
|
||||||
match el.tag {
|
"b" => apply_style_attributes("font-weight", "bold", self),
|
||||||
"b" => apply_style_attributes("font-weight", "bold", self),
|
"strong" => apply_style_attributes("font-weight", "bold", self),
|
||||||
"strong" => apply_style_attributes("font-weight", "bold", self),
|
"u" => apply_style_attributes("text-decoration", "underline", self),
|
||||||
"u" => apply_style_attributes("text-decoration", "underline", self),
|
"ins" => apply_style_attributes("text-decoration", "underline", self),
|
||||||
"ins" => apply_style_attributes("text-decoration", "underline", self),
|
"del" => apply_style_attributes("text-decoration", "line-through", self),
|
||||||
"del" => apply_style_attributes("text-decoration", "line-through", self),
|
"i" => apply_style_attributes("font-style", "italic", self),
|
||||||
"i" => apply_style_attributes("font-style", "italic", self),
|
"em" => apply_style_attributes("font-style", "italic", self),
|
||||||
"em" => apply_style_attributes("font-style", "italic", self),
|
"mark" => {
|
||||||
"mark" => apply_style_attributes(
|
apply_style_attributes("background-color", "rgba(241, 231, 64, 50%)", self)
|
||||||
"background-color",
|
|
||||||
"rgba(241, 231, 64, 50%)",
|
|
||||||
self,
|
|
||||||
),
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
_ => (),
|
||||||
|
|
||||||
// gather up all the styles from the attribute list
|
|
||||||
for &Attribute { name, value, .. } in el.attributes {
|
|
||||||
apply_style_attributes(name, value, self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
|
// gather up all the styles from the attribute list
|
||||||
|
for &Attribute { name, value, .. } in el.attributes {
|
||||||
|
apply_style_attributes(name, value, self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep the text styling from the parent element
|
// keep the text styling from the parent element
|
||||||
|
@ -125,7 +120,7 @@ impl Default for BorderEdge {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
color: None,
|
color: None,
|
||||||
style: BorderStyle::NONE,
|
style: BorderStyle::None,
|
||||||
width: UnitSystem::Point(0.0),
|
width: UnitSystem::Point(0.0),
|
||||||
radius: UnitSystem::Point(0.0),
|
radius: UnitSystem::Point(0.0),
|
||||||
}
|
}
|
||||||
|
@ -134,16 +129,16 @@ impl Default for BorderEdge {
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub enum BorderStyle {
|
pub enum BorderStyle {
|
||||||
DOTTED,
|
Dotted,
|
||||||
DASHED,
|
Dashed,
|
||||||
SOLID,
|
Solid,
|
||||||
DOUBLE,
|
Double,
|
||||||
GROOVE,
|
Groove,
|
||||||
RIDGE,
|
Ridge,
|
||||||
INSET,
|
Inset,
|
||||||
OUTSET,
|
Outset,
|
||||||
HIDDEN,
|
Hidden,
|
||||||
NONE,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BorderStyle {
|
impl BorderStyle {
|
||||||
|
@ -160,16 +155,16 @@ impl BorderStyle {
|
||||||
..NORMAL
|
..NORMAL
|
||||||
};
|
};
|
||||||
match self {
|
match self {
|
||||||
BorderStyle::DOTTED => Some(DOTTED),
|
BorderStyle::Dotted => Some(DOTTED),
|
||||||
BorderStyle::DASHED => Some(DASHED),
|
BorderStyle::Dashed => Some(DASHED),
|
||||||
BorderStyle::SOLID => Some(NORMAL),
|
BorderStyle::Solid => Some(NORMAL),
|
||||||
BorderStyle::DOUBLE => Some(DOUBLE),
|
BorderStyle::Double => Some(DOUBLE),
|
||||||
BorderStyle::GROOVE => Some(NORMAL),
|
BorderStyle::Groove => Some(NORMAL),
|
||||||
BorderStyle::RIDGE => Some(NORMAL),
|
BorderStyle::Ridge => Some(NORMAL),
|
||||||
BorderStyle::INSET => Some(NORMAL),
|
BorderStyle::Inset => Some(NORMAL),
|
||||||
BorderStyle::OUTSET => Some(NORMAL),
|
BorderStyle::Outset => Some(NORMAL),
|
||||||
BorderStyle::HIDDEN => None,
|
BorderStyle::Hidden => None,
|
||||||
BorderStyle::NONE => None,
|
BorderStyle::None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,16 +329,16 @@ fn apply_background(name: &str, value: &str, style: &mut StyleModifier) {
|
||||||
fn apply_border(name: &str, value: &str, style: &mut StyleModifier) {
|
fn apply_border(name: &str, value: &str, style: &mut StyleModifier) {
|
||||||
fn parse_border_style(v: &str) -> BorderStyle {
|
fn parse_border_style(v: &str) -> BorderStyle {
|
||||||
match v {
|
match v {
|
||||||
"dotted" => BorderStyle::DOTTED,
|
"dotted" => BorderStyle::Dotted,
|
||||||
"dashed" => BorderStyle::DASHED,
|
"dashed" => BorderStyle::Dashed,
|
||||||
"solid" => BorderStyle::SOLID,
|
"solid" => BorderStyle::Solid,
|
||||||
"double" => BorderStyle::DOUBLE,
|
"double" => BorderStyle::Double,
|
||||||
"groove" => BorderStyle::GROOVE,
|
"groove" => BorderStyle::Groove,
|
||||||
"ridge" => BorderStyle::RIDGE,
|
"ridge" => BorderStyle::Ridge,
|
||||||
"inset" => BorderStyle::INSET,
|
"inset" => BorderStyle::Inset,
|
||||||
"outset" => BorderStyle::OUTSET,
|
"outset" => BorderStyle::Outset,
|
||||||
"none" => BorderStyle::NONE,
|
"none" => BorderStyle::None,
|
||||||
"hidden" => BorderStyle::HIDDEN,
|
"hidden" => BorderStyle::Hidden,
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,7 +500,7 @@ fn apply_border(name: &str, value: &str, style: &mut StyleModifier) {
|
||||||
.zip(style.modifier.borders.slice().iter_mut())
|
.zip(style.modifier.borders.slice().iter_mut())
|
||||||
{
|
{
|
||||||
if let Some(w) = parse_value(v) {
|
if let Some(w) = parse_value(v) {
|
||||||
width.width = w.into();
|
width.width = w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ pub use dioxus_desktop as desktop;
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
pub use dioxus_tui as tui;
|
pub use dioxus_tui as tui;
|
||||||
|
|
||||||
|
#[cfg(feature = "native-core")]
|
||||||
|
pub use dioxus_native_core as native_core;
|
||||||
|
|
||||||
#[cfg(feature = "fermi")]
|
#[cfg(feature = "fermi")]
|
||||||
pub use fermi;
|
pub use fermi;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue