mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
rebase master
This commit is contained in:
parent
499971e9b3
commit
26d92b6e51
14 changed files with 529 additions and 318 deletions
|
@ -61,6 +61,7 @@ members = [
|
||||||
"packages/fermi",
|
"packages/fermi",
|
||||||
"packages/tui",
|
"packages/tui",
|
||||||
"packages/liveview",
|
"packages/liveview",
|
||||||
|
"packages/native-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -88,3 +89,6 @@ harness = false
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "jsframework"
|
name = "jsframework"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
|
@ -4,11 +4,54 @@ fn main() {
|
||||||
dioxus::tui::launch_cfg(
|
dioxus::tui::launch_cfg(
|
||||||
app,
|
app,
|
||||||
dioxus::tui::Config {
|
dioxus::tui::Config {
|
||||||
rendering_mode: dioxus::tui::RenderingMode::Ansi,
|
rendering_mode: dioxus::tui::RenderingMode::Rgb,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Props, PartialEq)]
|
||||||
|
struct BoxProps {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
hue: f32,
|
||||||
|
alpha: f32,
|
||||||
|
}
|
||||||
|
fn Box(cx: Scope<BoxProps>) -> Element {
|
||||||
|
let painted = use_state(&cx, || true);
|
||||||
|
|
||||||
|
// use_future(&cx, (), move |_| {
|
||||||
|
// let count = count.to_owned();
|
||||||
|
// let update = cx.schedule_update();
|
||||||
|
// async move {
|
||||||
|
// loop {
|
||||||
|
// count.with_mut(|i| *i += 1);
|
||||||
|
// tokio::time::sleep(std::time::Duration::from_millis(800)).await;
|
||||||
|
// update();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
let x = cx.props.x;
|
||||||
|
let y = cx.props.y;
|
||||||
|
let hue = cx.props.hue;
|
||||||
|
let current_painted = painted.get();
|
||||||
|
let alpha = cx.props.alpha + if *current_painted { 100.0 } else { 0.0 };
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
left: "{x}px",
|
||||||
|
top: "{y}px",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
background_color: "hsl({hue}, 100%, 50%, {alpha}%)",
|
||||||
|
align_items: "center",
|
||||||
|
onkeydown: |_| painted.with_mut(|i| *i = !*i),
|
||||||
|
onmouseenter: |_| painted.with_mut(|i| *i = !*i),
|
||||||
|
p{" "}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let steps = 50;
|
let steps = 50;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
|
@ -28,12 +71,11 @@ fn app(cx: Scope) -> Element {
|
||||||
{
|
{
|
||||||
let alpha = y as f32*100.0/steps as f32;
|
let alpha = y as f32*100.0/steps as f32;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
Box{
|
||||||
left: "{x}px",
|
x: x,
|
||||||
top: "{y}px",
|
y: y,
|
||||||
width: "10%",
|
alpha: alpha,
|
||||||
height: "100%",
|
hue: hue,
|
||||||
background_color: "hsl({hue}, 100%, 50%, {alpha}%)",
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,26 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
|
let alpha = use_state(&cx, || 100);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
|
onwheel: move |evt| alpha.set((**alpha + evt.data.delta_y as i64).min(100).max(0)),
|
||||||
|
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "10px",
|
height: "10px",
|
||||||
background_color: "red",
|
background_color: "red",
|
||||||
justify_content: "center",
|
// justify_content: "center",
|
||||||
align_items: "center",
|
// align_items: "center",
|
||||||
|
|
||||||
|
p{
|
||||||
|
color: "rgba(0, 255, 0, {alpha}%)",
|
||||||
"Hello world!"
|
"Hello world!"
|
||||||
}
|
}
|
||||||
|
p{
|
||||||
|
"{alpha}"
|
||||||
|
}
|
||||||
|
// p{"Hi"}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ fn app(cx: Scope) -> Element {
|
||||||
onwheel: move |evt| alpha.set((**alpha + evt.data.delta_y as i64).min(100).max(0)),
|
onwheel: move |evt| alpha.set((**alpha + evt.data.delta_y as i64).min(100).max(0)),
|
||||||
|
|
||||||
p {
|
p {
|
||||||
background_color: "black",
|
// background_color: "black",
|
||||||
flex_direction: "column",
|
flex_direction: "column",
|
||||||
justify_content: "center",
|
justify_content: "center",
|
||||||
align_items: "center",
|
align_items: "center",
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl BubbleState {
|
||||||
/// }
|
/// }
|
||||||
/// )).unwrap();
|
/// )).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UserEvent {
|
pub struct UserEvent {
|
||||||
/// The originator of the event trigger if available
|
/// The originator of the event trigger if available
|
||||||
pub scope_id: Option<ScopeId>,
|
pub scope_id: Option<ScopeId>,
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
- [ ] pub aspect_ratio: Number,
|
- [ ] pub aspect_ratio: Number,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use stretch2::{prelude::*, style::PositionType, style::Style};
|
use stretch2::{prelude::*, style::PositionType};
|
||||||
|
|
||||||
/// applies the entire html namespace defined in dioxus-html
|
/// applies the entire html namespace defined in dioxus-html
|
||||||
pub fn apply_layout_attributes(
|
pub fn apply_layout_attributes(
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
|
||||||
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
||||||
|
pub mod layout_attributes;
|
||||||
|
|
||||||
/// A tree that can sync with dioxus mutations backed by a hashmap.
|
/// A tree that can sync with dioxus mutations backed by a hashmap.
|
||||||
/// Intended for use in lazy native renderers with a state that passes from parrent to children and or accumulates state from children to parrents.
|
/// Intended for use in lazy native renderers with a state that passes from parrent to children and or accumulates state from children to parrents.
|
||||||
|
@ -10,7 +11,7 @@ use dioxus_core::{ElementId, Mutations, VNode, VirtualDom};
|
||||||
pub struct Tree<US: BubbledUpState = (), DS: PushedDownState = ()> {
|
pub struct Tree<US: BubbledUpState = (), DS: PushedDownState = ()> {
|
||||||
pub root: usize,
|
pub root: usize,
|
||||||
pub nodes: Vec<Option<TreeNode<US, DS>>>,
|
pub nodes: Vec<Option<TreeNode<US, DS>>>,
|
||||||
pub listeners: HashMap<usize, HashSet<&'static str>>,
|
pub nodes_listening: HashMap<&'static str, HashSet<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<US: BubbledUpState, DS: PushedDownState> Tree<US, DS> {
|
impl<US: BubbledUpState, DS: PushedDownState> Tree<US, DS> {
|
||||||
|
@ -29,7 +30,7 @@ impl<US: BubbledUpState, DS: PushedDownState> Tree<US, DS> {
|
||||||
)));
|
)));
|
||||||
v
|
v
|
||||||
},
|
},
|
||||||
listeners: HashMap::new(),
|
nodes_listening: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,17 +129,17 @@ impl<US: BubbledUpState, DS: PushedDownState> Tree<US, DS> {
|
||||||
scope: _,
|
scope: _,
|
||||||
root,
|
root,
|
||||||
} => {
|
} => {
|
||||||
if let Some(v) = self.listeners.get_mut(&(root as usize)) {
|
if let Some(v) = self.nodes_listening.get_mut(event_name) {
|
||||||
v.insert(event_name);
|
v.insert(root as usize);
|
||||||
} else {
|
} else {
|
||||||
let mut hs = HashSet::new();
|
let mut hs = HashSet::new();
|
||||||
hs.insert(event_name);
|
hs.insert(root as usize);
|
||||||
self.listeners.insert(root as usize, hs);
|
self.nodes_listening.insert(event_name, hs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RemoveEventListener { root, event } => {
|
RemoveEventListener { root, event } => {
|
||||||
let v = self.listeners.get_mut(&(root as usize)).unwrap();
|
let v = self.nodes_listening.get_mut(event).unwrap();
|
||||||
v.remove(event);
|
v.remove(&(root as usize));
|
||||||
}
|
}
|
||||||
SetText {
|
SetText {
|
||||||
root,
|
root,
|
||||||
|
@ -210,10 +211,8 @@ impl<US: BubbledUpState, DS: PushedDownState> Tree<US, DS> {
|
||||||
to_rerender.insert(id);
|
to_rerender.insert(id);
|
||||||
if let Some(p) = parent {
|
if let Some(p) = parent {
|
||||||
let i = to_bubble.partition_point(|(_, h)| *h < height - 1);
|
let i = to_bubble.partition_point(|(_, h)| *h < height - 1);
|
||||||
// println!("{i}");
|
|
||||||
// println!("{to_bubble:?}");
|
|
||||||
// make sure the parent is not already queued
|
// make sure the parent is not already queued
|
||||||
if to_bubble.len() == 0 || to_bubble.get(i).unwrap().0 != p.0 {
|
if i >= to_bubble.len() || to_bubble.get(i).unwrap().0 != p.0 {
|
||||||
to_bubble.insert(i, (p.0, height - 1));
|
to_bubble.insert(i, (p.0, height - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,10 +306,20 @@ impl<US: BubbledUpState, DS: PushedDownState> Tree<US, DS> {
|
||||||
fn get_mut(&mut self, id: usize) -> &mut TreeNode<US, DS> {
|
fn get_mut(&mut self, id: usize) -> &mut TreeNode<US, DS> {
|
||||||
self.nodes.get_mut(id).unwrap().as_mut().unwrap()
|
self.nodes.get_mut(id).unwrap().as_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&TreeNode<US, DS>> {
|
||||||
|
if let Some(nodes) = self.nodes_listening.get(event) {
|
||||||
|
let mut listening: Vec<_> = nodes.iter().map(|id| self.get(*id)).collect();
|
||||||
|
listening.sort_by(|n1, n2| (n1.height).cmp(&n2.height).reverse());
|
||||||
|
listening
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The node is stored client side and stores render data
|
/// The node is stored client side and stores render data
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TreeNode<US: BubbledUpState, DS: PushedDownState> {
|
pub struct TreeNode<US: BubbledUpState, DS: PushedDownState> {
|
||||||
pub id: ElementId,
|
pub id: ElementId,
|
||||||
pub parent: Option<ElementId>,
|
pub parent: Option<ElementId>,
|
||||||
|
@ -320,7 +329,7 @@ pub struct TreeNode<US: BubbledUpState, DS: PushedDownState> {
|
||||||
pub height: u16,
|
pub height: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TreeNodeType {
|
pub enum TreeNodeType {
|
||||||
Text {
|
Text {
|
||||||
text: String,
|
text: String,
|
||||||
|
@ -505,7 +514,7 @@ fn test_insert() {
|
||||||
)));
|
)));
|
||||||
v
|
v
|
||||||
},
|
},
|
||||||
listeners: HashMap::new(),
|
nodes_listening: HashMap::new(),
|
||||||
};
|
};
|
||||||
println!("{:?}", mutations);
|
println!("{:?}", mutations);
|
||||||
let to_update = tree.apply_mutations(vec![mutations.0]);
|
let to_update = tree.apply_mutations(vec![mutations.0]);
|
||||||
|
|
|
@ -4,12 +4,12 @@ use crossterm::event::{
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
|
|
||||||
use dioxus_html::{on::*, KeyCode};
|
use dioxus_html::{on::*, KeyCode};
|
||||||
use dioxus_native_core::{Tree, TreeNodeType};
|
use dioxus_native_core::{Tree, TreeNode};
|
||||||
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
|
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::HashSet,
|
collections::{HashMap, HashSet},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
|
@ -163,12 +163,35 @@ impl InnerInputState {
|
||||||
|
|
||||||
fn update<'a>(
|
fn update<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
dom: &'a VirtualDom,
|
|
||||||
evts: &mut Vec<EventCore>,
|
evts: &mut Vec<EventCore>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
layout: &Stretch,
|
layout: &Stretch,
|
||||||
layouts: &mut Tree<RinkLayout, StyleModifier>,
|
tree: &mut Tree<RinkLayout, StyleModifier>,
|
||||||
node: &'a VNode<'a>,
|
) {
|
||||||
|
let previous_mouse = self
|
||||||
|
.mouse
|
||||||
|
.as_ref()
|
||||||
|
.map(|m| (clone_mouse_data(&m.0), m.1.clone()));
|
||||||
|
|
||||||
|
self.wheel = None;
|
||||||
|
|
||||||
|
for e in evts.iter_mut() {
|
||||||
|
self.apply_event(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.resolve_mouse_events(previous_mouse, resolved_events, layout, tree);
|
||||||
|
|
||||||
|
// for s in &self.subscribers {
|
||||||
|
// s();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_mouse_events(
|
||||||
|
&self,
|
||||||
|
previous_mouse: Option<(MouseData, Vec<u16>)>,
|
||||||
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
|
layout: &Stretch,
|
||||||
|
tree: &mut Tree<RinkLayout, StyleModifier>,
|
||||||
) {
|
) {
|
||||||
struct Data<'b> {
|
struct Data<'b> {
|
||||||
new_pos: (i32, i32),
|
new_pos: (i32, i32),
|
||||||
|
@ -187,136 +210,31 @@ impl InnerInputState {
|
||||||
&& layout.location.y as i32 + layout.size.height as i32 >= point.1
|
&& layout.location.y as i32 + layout.size.height as i32 >= point.1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mouse_events<'c, 'd>(
|
fn try_create_event(
|
||||||
dom: &'c VirtualDom,
|
name: &'static str,
|
||||||
|
data: Arc<dyn Any + Send + Sync>,
|
||||||
|
will_bubble: &mut HashSet<ElementId>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
layout: &Stretch,
|
node: &TreeNode<RinkLayout, StyleModifier>,
|
||||||
layouts: &Tree<RinkLayout, StyleModifier>,
|
tree: &Tree<RinkLayout, StyleModifier>,
|
||||||
node: &'c VNode<'c>,
|
) {
|
||||||
data: &'d Data<'d>,
|
|
||||||
) -> HashSet<&'static str> {
|
|
||||||
match node {
|
|
||||||
VNode::Fragment(f) => {
|
|
||||||
let mut union = HashSet::new();
|
|
||||||
for child in f.children {
|
|
||||||
union = union
|
|
||||||
.union(&get_mouse_events(
|
|
||||||
dom,
|
|
||||||
resolved_events,
|
|
||||||
layout,
|
|
||||||
layouts,
|
|
||||||
child,
|
|
||||||
data,
|
|
||||||
))
|
|
||||||
.copied()
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
return union;
|
|
||||||
}
|
|
||||||
|
|
||||||
VNode::Component(vcomp) => {
|
|
||||||
let idx = vcomp.scope.get().unwrap();
|
|
||||||
let new_node = dom.get_scope(idx).unwrap().root_node();
|
|
||||||
return get_mouse_events(dom, resolved_events, layout, layouts, new_node, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
VNode::Placeholder(_) => return HashSet::new(),
|
|
||||||
|
|
||||||
VNode::Element(_) | VNode::Text(_) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = node.try_mounted_id().unwrap();
|
|
||||||
let node = layouts.get(id.0);
|
|
||||||
|
|
||||||
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
|
||||||
|
|
||||||
let previously_contained = data
|
|
||||||
.old_pos
|
|
||||||
.filter(|pos| layout_contains_point(node_layout, *pos))
|
|
||||||
.is_some();
|
|
||||||
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
|
||||||
|
|
||||||
match &node.node_type {
|
|
||||||
TreeNodeType::Element { children, .. } => {
|
|
||||||
let mut events = HashSet::new();
|
|
||||||
if previously_contained || currently_contains {
|
|
||||||
for c in children {
|
|
||||||
events = events
|
|
||||||
.union(&get_mouse_events(
|
|
||||||
dom,
|
|
||||||
resolved_events,
|
|
||||||
layout,
|
|
||||||
layouts,
|
|
||||||
dom.get_element(*c).unwrap(),
|
|
||||||
data,
|
|
||||||
))
|
|
||||||
.copied()
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut try_create_event = |name| {
|
|
||||||
// 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
|
||||||
if events.insert(name) {
|
if will_bubble.insert(node.id) {
|
||||||
|
let mut parent = node.parent;
|
||||||
|
while let Some(parent_id) = parent {
|
||||||
|
will_bubble.insert(parent_id);
|
||||||
|
parent = tree.get(parent_id.0).parent;
|
||||||
|
}
|
||||||
resolved_events.push(UserEvent {
|
resolved_events.push(UserEvent {
|
||||||
scope_id: None,
|
scope_id: None,
|
||||||
priority: EventPriority::Medium,
|
priority: EventPriority::Medium,
|
||||||
name,
|
name,
|
||||||
element: Some(node.id),
|
element: Some(node.id),
|
||||||
data: Arc::new(clone_mouse_data(data.mouse_data)),
|
data: data,
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if currently_contains {
|
|
||||||
if !previously_contained {
|
|
||||||
try_create_event("mouseenter");
|
|
||||||
try_create_event("mouseover");
|
|
||||||
}
|
|
||||||
if data.clicked {
|
|
||||||
try_create_event("mousedown");
|
|
||||||
}
|
|
||||||
if data.released {
|
|
||||||
try_create_event("mouseup");
|
|
||||||
match data.mouse_data.button {
|
|
||||||
0 => try_create_event("click"),
|
|
||||||
2 => try_create_event("contextmenu"),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(w) = data.wheel_data {
|
|
||||||
if data.wheel_delta != 0.0 {
|
|
||||||
resolved_events.push(UserEvent {
|
|
||||||
scope_id: None,
|
|
||||||
priority: EventPriority::Medium,
|
|
||||||
name: "wheel",
|
|
||||||
element: Some(node.id),
|
|
||||||
data: Arc::new(clone_wheel_data(w)),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if previously_contained {
|
|
||||||
try_create_event("mouseleave");
|
|
||||||
try_create_event("mouseout");
|
|
||||||
}
|
|
||||||
events
|
|
||||||
}
|
|
||||||
TreeNodeType::Text { .. } => HashSet::new(),
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let previous_mouse = self
|
|
||||||
.mouse
|
|
||||||
.as_ref()
|
|
||||||
.map(|m| (clone_mouse_data(&m.0), m.1.clone()));
|
|
||||||
// println!("{previous_mouse:?}");
|
|
||||||
|
|
||||||
self.wheel = None;
|
|
||||||
|
|
||||||
for e in evts.iter_mut() {
|
|
||||||
self.apply_event(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve hover events
|
|
||||||
if let Some(mouse) = &self.mouse {
|
if let Some(mouse) = &self.mouse {
|
||||||
let new_pos = (mouse.0.screen_x, mouse.0.screen_y);
|
let new_pos = (mouse.0.screen_x, mouse.0.screen_y);
|
||||||
let old_pos = previous_mouse
|
let old_pos = previous_mouse
|
||||||
|
@ -338,12 +256,245 @@ impl InnerInputState {
|
||||||
mouse_data,
|
mouse_data,
|
||||||
wheel_data,
|
wheel_data,
|
||||||
};
|
};
|
||||||
get_mouse_events(dom, resolved_events, layout, layouts, node, &data);
|
|
||||||
|
{
|
||||||
|
// mousemove
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("mousemove") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let previously_contained = data
|
||||||
|
.old_pos
|
||||||
|
.filter(|pos| layout_contains_point(node_layout, *pos))
|
||||||
|
.is_some();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if previously_contained {
|
||||||
|
try_create_event(
|
||||||
|
"mousemove",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for s in &self.subscribers {
|
{
|
||||||
// s();
|
// mouseenter
|
||||||
// }
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("mouseenter") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let previously_contained = data
|
||||||
|
.old_pos
|
||||||
|
.filter(|pos| layout_contains_point(node_layout, *pos))
|
||||||
|
.is_some();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if !previously_contained {
|
||||||
|
try_create_event(
|
||||||
|
"mouseenter",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// mouseover
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("mouseover") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let previously_contained = data
|
||||||
|
.old_pos
|
||||||
|
.filter(|pos| layout_contains_point(node_layout, *pos))
|
||||||
|
.is_some();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if !previously_contained {
|
||||||
|
try_create_event(
|
||||||
|
"mouseover",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// mousedown
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("mousedown") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if data.clicked {
|
||||||
|
try_create_event(
|
||||||
|
"mousedown",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// mouseup
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("mouseup") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if data.released {
|
||||||
|
try_create_event(
|
||||||
|
"mouseup",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// click
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("click") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if data.released && data.mouse_data.button == 0 {
|
||||||
|
try_create_event(
|
||||||
|
"click",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// contextmenu
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("contextmenu") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if data.released && data.mouse_data.button == 2 {
|
||||||
|
try_create_event(
|
||||||
|
"contextmenu",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// wheel
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("wheel") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if currently_contains {
|
||||||
|
if let Some(w) = data.wheel_data {
|
||||||
|
if data.wheel_delta != 0.0 {
|
||||||
|
try_create_event(
|
||||||
|
"wheel",
|
||||||
|
Arc::new(clone_wheel_data(w)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// mouseleave
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("mouseleave") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let previously_contained = data
|
||||||
|
.old_pos
|
||||||
|
.filter(|pos| layout_contains_point(node_layout, *pos))
|
||||||
|
.is_some();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if !currently_contains && previously_contained {
|
||||||
|
try_create_event(
|
||||||
|
"mouseleave",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// mouseout
|
||||||
|
let mut will_bubble = HashSet::new();
|
||||||
|
for node in tree.get_listening_sorted("mouseout") {
|
||||||
|
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
|
||||||
|
let previously_contained = data
|
||||||
|
.old_pos
|
||||||
|
.filter(|pos| layout_contains_point(node_layout, *pos))
|
||||||
|
.is_some();
|
||||||
|
let currently_contains = layout_contains_point(node_layout, data.new_pos);
|
||||||
|
|
||||||
|
if !currently_contains && previously_contained {
|
||||||
|
try_create_event(
|
||||||
|
"mouseout",
|
||||||
|
Arc::new(clone_mouse_data(data.mouse_data)),
|
||||||
|
&mut will_bubble,
|
||||||
|
resolved_events,
|
||||||
|
node,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn subscribe(&mut self, f: Rc<dyn Fn() + 'static>) {
|
// fn subscribe(&mut self, f: Rc<dyn Fn() + 'static>) {
|
||||||
|
@ -364,7 +515,7 @@ impl RinkInputHandler {
|
||||||
cx: &ScopeState,
|
cx: &ScopeState,
|
||||||
) -> (Self, Rc<RefCell<InnerInputState>>) {
|
) -> (Self, Rc<RefCell<InnerInputState>>) {
|
||||||
let queued_events = Rc::new(RefCell::new(Vec::new()));
|
let queued_events = Rc::new(RefCell::new(Vec::new()));
|
||||||
let queued_events2 = Rc::<RefCell<std::vec::Vec<_>>>::downgrade(&queued_events);
|
let queued_events2 = Rc::downgrade(&queued_events);
|
||||||
|
|
||||||
cx.push_future(async move {
|
cx.push_future(async move {
|
||||||
while let Some(evt) = receiver.next().await {
|
while let Some(evt) = receiver.next().await {
|
||||||
|
@ -391,68 +542,63 @@ impl RinkInputHandler {
|
||||||
|
|
||||||
pub fn get_events<'a>(
|
pub fn get_events<'a>(
|
||||||
&self,
|
&self,
|
||||||
dom: &'a VirtualDom,
|
|
||||||
layout: &Stretch,
|
layout: &Stretch,
|
||||||
layouts: &mut Tree<RinkLayout, StyleModifier>,
|
tree: &mut Tree<RinkLayout, StyleModifier>,
|
||||||
node: &'a VNode<'a>,
|
|
||||||
) -> Vec<UserEvent> {
|
) -> Vec<UserEvent> {
|
||||||
|
let mut resolved_events = Vec::new();
|
||||||
|
|
||||||
|
(*self.state).borrow_mut().update(
|
||||||
|
&mut (*self.queued_events).borrow_mut(),
|
||||||
|
&mut resolved_events,
|
||||||
|
layout,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
|
||||||
|
let events = self
|
||||||
|
.queued_events
|
||||||
|
.replace(Vec::new())
|
||||||
|
.into_iter()
|
||||||
|
// these events were added in the update stage
|
||||||
|
.filter(|e| {
|
||||||
|
![
|
||||||
|
"mouseenter",
|
||||||
|
"mouseover",
|
||||||
|
"mouseleave",
|
||||||
|
"mouseout",
|
||||||
|
"mousedown",
|
||||||
|
"mouseup",
|
||||||
|
"mousemove",
|
||||||
|
"drag",
|
||||||
|
"wheel",
|
||||||
|
"click",
|
||||||
|
"contextmenu",
|
||||||
|
]
|
||||||
|
.contains(&e.0)
|
||||||
|
})
|
||||||
|
.map(|evt| (evt.0, evt.1.into_any()));
|
||||||
|
|
||||||
// todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
|
// todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
|
||||||
fn inner(
|
let mut hm: HashMap<&'static str, Vec<Arc<dyn Any + Send + Sync>>> = HashMap::new();
|
||||||
queue: &[(&'static str, Arc<dyn Any + Send + Sync>)],
|
for (event, data) in events {
|
||||||
resolved: &mut Vec<UserEvent>,
|
if let Some(v) = hm.get_mut(event) {
|
||||||
node: &VNode,
|
v.push(data);
|
||||||
) {
|
} else {
|
||||||
match node {
|
hm.insert(event, vec![data]);
|
||||||
VNode::Fragment(frag) => {
|
|
||||||
for c in frag.children {
|
|
||||||
inner(queue, resolved, c);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VNode::Element(el) => {
|
for (event, datas) in hm {
|
||||||
for l in el.listeners {
|
for node in tree.get_listening_sorted(event) {
|
||||||
for (name, data) in queue.iter() {
|
for data in &datas {
|
||||||
if *name == l.event {
|
resolved_events.push(UserEvent {
|
||||||
if let Some(id) = el.id.get() {
|
|
||||||
resolved.push(UserEvent {
|
|
||||||
scope_id: None,
|
scope_id: None,
|
||||||
priority: EventPriority::Medium,
|
priority: EventPriority::Medium,
|
||||||
name: *name,
|
name: event,
|
||||||
element: Some(id),
|
element: Some(node.id),
|
||||||
data: data.clone(),
|
data: data.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for c in el.children {
|
|
||||||
inner(queue, resolved, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut resolved_events = Vec::new();
|
|
||||||
|
|
||||||
(*self.state).borrow_mut().update(
|
|
||||||
dom,
|
|
||||||
&mut (*self.queued_events).borrow_mut(),
|
|
||||||
&mut resolved_events,
|
|
||||||
layout,
|
|
||||||
layouts,
|
|
||||||
node,
|
|
||||||
);
|
|
||||||
|
|
||||||
let events: Vec<_> = self
|
|
||||||
.queued_events
|
|
||||||
.replace(Vec::new())
|
|
||||||
.into_iter()
|
|
||||||
// these events were added in the update stage
|
|
||||||
.filter(|e| !["mousedown", "mouseup", "mousemove", "drag", "wheel"].contains(&e.0))
|
|
||||||
.map(|e| (e.0, e.1.into_any()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
inner(&events, &mut resolved_events, node);
|
|
||||||
|
|
||||||
resolved_events
|
resolved_events
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use dioxus_core::*;
|
||||||
use dioxus_native_core::BubbledUpState;
|
use dioxus_native_core::BubbledUpState;
|
||||||
use stretch2::prelude::*;
|
use stretch2::prelude::*;
|
||||||
|
|
||||||
use crate::layout_attributes::apply_layout_attributes;
|
use dioxus_native_core::layout_attributes::apply_layout_attributes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The layout system uses the lineheight as one point.
|
The layout system uses the lineheight as one point.
|
||||||
|
@ -42,7 +42,6 @@ impl BubbledUpState for RinkLayout {
|
||||||
|
|
||||||
if let Some(n) = self.node {
|
if let Some(n) = self.node {
|
||||||
if self.style != style {
|
if self.style != style {
|
||||||
panic!("new style: {style:?}");
|
|
||||||
stretch.set_style(n, style).unwrap();
|
stretch.set_style(n, style).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,11 +73,9 @@ impl BubbledUpState for RinkLayout {
|
||||||
|
|
||||||
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 {
|
||||||
panic!("new children: {child_layout:?}");
|
|
||||||
stretch.set_children(n, &child_layout).unwrap();
|
stretch.set_children(n, &child_layout).unwrap();
|
||||||
}
|
}
|
||||||
if self.style != style {
|
if self.style != style {
|
||||||
panic!("new style: {style:?}");
|
|
||||||
stretch.set_style(n, style).unwrap();
|
stretch.set_style(n, style).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// 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},
|
||||||
|
@ -6,23 +9,19 @@ use crossterm::{
|
||||||
};
|
};
|
||||||
use dioxus_core::exports::futures_channel::mpsc::unbounded;
|
use dioxus_core::exports::futures_channel::mpsc::unbounded;
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
|
use dioxus_html::on::{KeyboardData, MouseData, PointerData, TouchData, WheelData};
|
||||||
use dioxus_native_core::Tree;
|
use dioxus_native_core::Tree;
|
||||||
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt};
|
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt};
|
||||||
use std::{
|
use std::collections::HashSet;
|
||||||
io,
|
use std::{io, time::Duration};
|
||||||
time::{Duration, Instant},
|
use stretch2::{prelude::Size, Stretch};
|
||||||
};
|
|
||||||
use stretch2::{
|
|
||||||
prelude::{Layout, Size},
|
|
||||||
Stretch,
|
|
||||||
};
|
|
||||||
use style_attributes::StyleModifier;
|
use style_attributes::StyleModifier;
|
||||||
|
use tokio::time::Instant;
|
||||||
use tui::{backend::CrosstermBackend, Terminal};
|
use tui::{backend::CrosstermBackend, Terminal};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod hooks;
|
mod hooks;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod layout_attributes;
|
|
||||||
mod render;
|
mod render;
|
||||||
mod style;
|
mod style;
|
||||||
mod style_attributes;
|
mod style_attributes;
|
||||||
|
@ -31,7 +30,6 @@ mod widget;
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
pub use hooks::*;
|
pub use hooks::*;
|
||||||
pub use layout::*;
|
pub use layout::*;
|
||||||
pub use layout_attributes::*;
|
|
||||||
pub use render::*;
|
pub use render::*;
|
||||||
|
|
||||||
pub fn launch(app: Component<()>) {
|
pub fn launch(app: Component<()>) {
|
||||||
|
@ -104,6 +102,12 @@ pub fn render_vdom(
|
||||||
let mut terminal = Terminal::new(backend).unwrap();
|
let mut terminal = Terminal::new(backend).unwrap();
|
||||||
|
|
||||||
terminal.clear().unwrap();
|
terminal.clear().unwrap();
|
||||||
|
let mut to_rerender: HashSet<usize> = tree
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|n| n.as_ref())
|
||||||
|
.map(|n| n.id.0)
|
||||||
|
.collect();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
/*
|
/*
|
||||||
|
@ -115,20 +119,14 @@ pub fn render_vdom(
|
||||||
|
|
||||||
use simd to compare lines for diffing?
|
use simd to compare lines for diffing?
|
||||||
|
|
||||||
|
todo: lazy re-rendering
|
||||||
todo: reuse the layout and node objects.
|
|
||||||
our work_with_deadline method can tell us which nodes are dirty.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
if !to_rerender.is_empty() {
|
||||||
Compute the layout given the terminal size
|
|
||||||
*/
|
|
||||||
let mut events = Vec::new();
|
|
||||||
|
|
||||||
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();
|
||||||
println!("{dims:?}");
|
// println!("{dims:?}");
|
||||||
let width = dims.width;
|
let width = dims.width;
|
||||||
let height = dims.height;
|
let height = dims.height;
|
||||||
let root_id = tree.root;
|
let root_id = tree.root;
|
||||||
|
@ -142,27 +140,30 @@ pub fn render_vdom(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let root = tree.get(tree.root);
|
||||||
|
render::render_vnode(frame, &stretch, &tree, &root, cfg);
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
// resolve events before rendering
|
// resolve events before rendering
|
||||||
events = handler.get_events(
|
// todo: events do not trigger update?
|
||||||
vdom,
|
for e in handler.get_events(&stretch, &mut tree) {
|
||||||
&stretch,
|
let tname = if e.data.is::<PointerData>() {
|
||||||
&mut tree,
|
"PointerData"
|
||||||
vdom.base_scope().root_node(),
|
} else if e.data.is::<WheelData>() {
|
||||||
);
|
"WheelData"
|
||||||
for n in &tree.nodes {
|
} else if e.data.is::<MouseData>() {
|
||||||
if let Some(node) = n {
|
"MouseData"
|
||||||
let Layout { location, size, .. } =
|
} else if e.data.is::<KeyboardData>() {
|
||||||
stretch.layout(node.up_state.node.unwrap()).unwrap();
|
"KeyboardData"
|
||||||
println!("{node:#?}");
|
} else if e.data.is::<TouchData>() {
|
||||||
println!("\t{location:?}: {size:?}");
|
"TouchData"
|
||||||
}
|
} else if e.data.is::<(u16, u16)>() {
|
||||||
}
|
"(u16, u16)"
|
||||||
let root = tree.get(tree.root);
|
} else {
|
||||||
// render::render_vnode(frame, &stretch, &tree, &root, cfg);
|
panic!()
|
||||||
})?;
|
};
|
||||||
|
// println!("{tname}: {e:?}");
|
||||||
for e in events {
|
|
||||||
vdom.handle_message(SchedulerMsg::Event(e));
|
vdom.handle_message(SchedulerMsg::Event(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +200,10 @@ pub fn render_vdom(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mutations = vdom.work_with_deadline(|| false);
|
let mutations = vdom.work_with_deadline(|| false);
|
||||||
|
// updates the tree's nodes
|
||||||
let to_update = tree.apply_mutations(mutations);
|
let to_update = tree.apply_mutations(mutations);
|
||||||
let _to_rerender = tree
|
// update the style and layout
|
||||||
|
to_rerender = tree
|
||||||
.update_state(&vdom, to_update, &mut stretch, &mut ())
|
.update_state(&vdom, to_update, &mut stretch, &mut ())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -220,7 +223,6 @@ pub fn render_vdom(
|
||||||
enum InputEvent {
|
enum InputEvent {
|
||||||
UserInput(TermEvent),
|
UserInput(TermEvent),
|
||||||
Tick,
|
Tick,
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
Close,
|
Close,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use dioxus_native_core::{Tree, TreeNode};
|
use dioxus_native_core::{layout_attributes::UnitSystem, Tree, TreeNode};
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
use stretch2::{
|
use stretch2::{
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
style::{RinkColor, RinkStyle},
|
style::{RinkColor, RinkStyle},
|
||||||
style_attributes::{BorderEdge, BorderStyle},
|
style_attributes::{BorderEdge, BorderStyle},
|
||||||
widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
|
widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
|
||||||
Config, RinkLayout, StyleModifier, UnitSystem,
|
Config, RinkLayout, StyleModifier,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
||||||
|
@ -48,7 +48,7 @@ pub fn render_vnode<'a>(
|
||||||
// println!("{:?}", self.style);
|
// println!("{:?}", self.style);
|
||||||
new_cell.set_style(self.style);
|
new_cell.set_style(self.style);
|
||||||
new_cell.symbol = c.to_string();
|
new_cell.symbol = c.to_string();
|
||||||
buf.set(area.left() + i as u16, area.top(), &new_cell);
|
buf.set(area.left() + i as u16, area.top(), new_cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
|
||||||
buf.set(
|
buf.set(
|
||||||
(current[0] + pos[0] as i32) as u16,
|
(current[0] + pos[0] as i32) as u16,
|
||||||
(current[1] + pos[1] as i32) as u16,
|
(current[1] + pos[1] as i32) as u16,
|
||||||
&new_cell,
|
new_cell,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
|
||||||
if let Some(c) = self.down_state.style.bg {
|
if let Some(c) = self.down_state.style.bg {
|
||||||
new_cell.bg = c;
|
new_cell.bg = c;
|
||||||
}
|
}
|
||||||
buf.set(x, y, &new_cell);
|
buf.set(x, y, new_cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
|
||||||
}
|
}
|
||||||
for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
|
for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
|
||||||
new_cell.symbol = symbols.horizontal.to_string();
|
new_cell.symbol = symbols.horizontal.to_string();
|
||||||
buf.set(x, area.top(), &new_cell);
|
buf.set(x, area.top(), new_cell.clone());
|
||||||
}
|
}
|
||||||
draw_arc(
|
draw_arc(
|
||||||
[area.right() - radius[0] - 1, area.top() + radius[1]],
|
[area.right() - radius[0] - 1, area.top() + radius[1]],
|
||||||
|
@ -330,7 +330,7 @@ impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
|
||||||
}
|
}
|
||||||
for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
|
for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
|
||||||
new_cell.symbol = symbols.vertical.to_string();
|
new_cell.symbol = symbols.vertical.to_string();
|
||||||
buf.set(area.right() - 1, y, &new_cell);
|
buf.set(area.right() - 1, y, new_cell.clone());
|
||||||
}
|
}
|
||||||
draw_arc(
|
draw_arc(
|
||||||
[area.right() - radius[0] - 1, area.bottom() - radius[1] - 1],
|
[area.right() - radius[0] - 1, area.bottom() - radius[1] - 1],
|
||||||
|
@ -365,7 +365,7 @@ impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
|
||||||
}
|
}
|
||||||
for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
|
for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
|
||||||
new_cell.symbol = symbols.horizontal.to_string();
|
new_cell.symbol = symbols.horizontal.to_string();
|
||||||
buf.set(x, area.bottom() - 1, &new_cell);
|
buf.set(x, area.bottom() - 1, new_cell.clone());
|
||||||
}
|
}
|
||||||
draw_arc(
|
draw_arc(
|
||||||
[area.left() + radius[0], area.bottom() - radius[1] - 1],
|
[area.left() + radius[0], area.bottom() - radius[1] - 1],
|
||||||
|
@ -400,7 +400,7 @@ impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
|
||||||
}
|
}
|
||||||
for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
|
for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
|
||||||
new_cell.symbol = symbols.vertical.to_string();
|
new_cell.symbol = symbols.vertical.to_string();
|
||||||
buf.set(area.left(), y, &new_cell);
|
buf.set(area.left(), y, new_cell.clone());
|
||||||
}
|
}
|
||||||
draw_arc(
|
draw_arc(
|
||||||
[area.left() + radius[0], area.top() + radius[1]],
|
[area.left() + radius[0], area.top() + radius[1]],
|
||||||
|
|
|
@ -30,14 +30,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use dioxus_core::{Attribute, VNode};
|
use dioxus_core::{Attribute, VNode};
|
||||||
use dioxus_native_core::PushedDownState;
|
use dioxus_native_core::{
|
||||||
|
layout_attributes::{parse_value, UnitSystem},
|
||||||
use crate::{
|
PushedDownState,
|
||||||
parse_value,
|
|
||||||
style::{RinkColor, RinkStyle},
|
|
||||||
UnitSystem,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::style::{RinkColor, RinkStyle};
|
||||||
|
|
||||||
#[derive(Default, Clone, PartialEq, Debug)]
|
#[derive(Default, Clone, PartialEq, Debug)]
|
||||||
pub struct StyleModifier {
|
pub struct StyleModifier {
|
||||||
pub style: RinkStyle,
|
pub style: RinkStyle,
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl<'a> RinkBuffer<'a> {
|
||||||
Self { buf, cfg }
|
Self { buf, cfg }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, x: u16, y: u16, new: &RinkCell) {
|
pub fn set(&mut self, x: u16, y: u16, new: RinkCell) {
|
||||||
let area = self.buf.area();
|
let area = self.buf.area();
|
||||||
if x < area.x || x > area.width || y < area.y || y > area.height {
|
if x < area.x || x > area.width || y < area.y || y > area.height {
|
||||||
panic!("({x}, {y}) is not in {area:?}");
|
panic!("({x}, {y}) is not in {area:?}");
|
||||||
|
@ -34,7 +34,7 @@ impl<'a> RinkBuffer<'a> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cell.modifier = new.modifier;
|
cell.modifier = new.modifier;
|
||||||
cell.symbol = new.symbol.clone();
|
cell.symbol = new.symbol;
|
||||||
cell.fg = convert(self.cfg.rendering_mode, new.fg.blend(cell.bg));
|
cell.fg = convert(self.cfg.rendering_mode, new.fg.blend(cell.bg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use stretch::style::Dimension;
|
||||||
use stretch2 as stretch;
|
use stretch2 as stretch;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -6,14 +7,7 @@ fn relayout() {
|
||||||
let node1 = stretch
|
let node1 = stretch
|
||||||
.new_node(
|
.new_node(
|
||||||
stretch::style::Style {
|
stretch::style::Style {
|
||||||
position: stretch::geometry::Point {
|
size: stretch::geometry::Size { width: Dimension::Points(8f32), height: Dimension::Points(80f32) },
|
||||||
x: stretch::style::Dimension::Points(10f32),
|
|
||||||
y: stretch::style::Dimension::Points(10f32),
|
|
||||||
},
|
|
||||||
size: stretch::geometry::Size {
|
|
||||||
width: stretch::style::Dimension::Points(10f32),
|
|
||||||
height: stretch::style::Dimension::Points(10f32),
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&[],
|
&[],
|
||||||
|
@ -22,10 +16,9 @@ fn relayout() {
|
||||||
let node0 = stretch
|
let node0 = stretch
|
||||||
.new_node(
|
.new_node(
|
||||||
stretch::style::Style {
|
stretch::style::Style {
|
||||||
size: stretch::geometry::Size {
|
align_self: stretch::prelude::AlignSelf::Center,
|
||||||
width: stretch::style::Dimension::Percent(1f32),
|
size: stretch::geometry::Size { width: Dimension::Auto, height: Dimension::Auto },
|
||||||
height: stretch::style::Dimension::Percent(1f32),
|
// size: stretch::geometry::Size { width: Dimension::Percent(1.0), height: Dimension::Percent(1.0) },
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&[node1],
|
&[node1],
|
||||||
|
@ -34,30 +27,38 @@ fn relayout() {
|
||||||
let node = stretch
|
let node = stretch
|
||||||
.new_node(
|
.new_node(
|
||||||
stretch::style::Style {
|
stretch::style::Style {
|
||||||
size: stretch::geometry::Size {
|
size: stretch::geometry::Size { width: Dimension::Percent(1f32), height: Dimension::Percent(1f32) },
|
||||||
width: stretch::style::Dimension::Points(100f32),
|
|
||||||
height: stretch::style::Dimension::Points(100f32),
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&[node0],
|
&[node0],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
for _ in 0..10 {
|
println!("0:");
|
||||||
stretch
|
stretch
|
||||||
.compute_layout(node, stretch::geometry::Size::undefined())
|
.compute_layout(
|
||||||
|
node,
|
||||||
|
stretch::geometry::Size {
|
||||||
|
width: stretch::prelude::Number::Defined(100f32),
|
||||||
|
height: stretch::prelude::Number::Defined(100f32),
|
||||||
|
},
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
|
let initial = stretch.layout(node).unwrap().location;
|
||||||
assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
|
let initial0 = stretch.layout(node0).unwrap().location;
|
||||||
assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
|
let initial1 = stretch.layout(node1).unwrap().location;
|
||||||
assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
|
for i in 1..10 {
|
||||||
assert_eq!(stretch.layout(node1).unwrap().size.width, 10f32);
|
println!("\n\n{i}:");
|
||||||
assert_eq!(stretch.layout(node1).unwrap().size.height, 10f32);
|
stretch
|
||||||
assert_eq!(stretch.layout(node1).unwrap().location.x, 0f32);
|
.compute_layout(
|
||||||
assert_eq!(stretch.layout(node1).unwrap().location.y, 0f32);
|
node,
|
||||||
assert_eq!(stretch.layout(node0).unwrap().size.width, 100f32);
|
stretch::geometry::Size {
|
||||||
assert_eq!(stretch.layout(node0).unwrap().size.height, 100f32);
|
width: stretch::prelude::Number::Defined(100f32),
|
||||||
assert_eq!(stretch.layout(node0).unwrap().location.x, 0f32);
|
height: stretch::prelude::Number::Defined(100f32),
|
||||||
assert_eq!(stretch.layout(node0).unwrap().location.y, 0f32);
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(stretch.layout(node).unwrap().location, initial);
|
||||||
|
assert_eq!(stretch.layout(node0).unwrap().location, initial0);
|
||||||
|
assert_eq!(stretch.layout(node1).unwrap().location, initial1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue