rebase master
This commit is contained in:
1 parent
499971e9b3
commit
26d92b6e51
14 files changed
+529
-318
No files matched your search
+329
-183
@@ -4,12 +4,12 @@ use crossterm::event::{
|
||||
use dioxus_core::*;
|
||||
|
||||
use dioxus_html::{on::*, KeyCode};
|
||||
use dioxus_native_core::{Tree, TreeNodeType};
|
||||
use dioxus_native_core::{Tree, TreeNode};
|
||||
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
collections::HashSet,
|
||||
collections::{HashMap, HashSet},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
@@ -163,12 +163,35 @@ impl InnerInputState {
|
||||
|
||||
fn update<'a>(
|
||||
&mut self,
|
||||
dom: &'a VirtualDom,
|
||||
evts: &mut Vec<EventCore>,
|
||||
resolved_events: &mut Vec<UserEvent>,
|
||||
layout: &Stretch,
|
||||
layouts: &mut Tree<RinkLayout, StyleModifier>,
|
||||
node: &'a VNode<'a>,
|
||||
tree: &mut Tree<RinkLayout, StyleModifier>,
|
||||
) {
|
||||
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> {
|
||||
new_pos: (i32, i32),
|
||||
@@ -187,136 +210,31 @@ impl InnerInputState {
|
||||
&& layout.location.y as i32 + layout.size.height as i32 >= point.1
|
||||
}
|
||||
|
||||
fn get_mouse_events<'c, 'd>(
|
||||
dom: &'c VirtualDom,
|
||||
fn try_create_event(
|
||||
name: &'static str,
|
||||
data: Arc<dyn Any + Send + Sync>,
|
||||
will_bubble: &mut HashSet<ElementId>,
|
||||
resolved_events: &mut Vec<UserEvent>,
|
||||
layout: &Stretch,
|
||||
layouts: &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;
|
||||
node: &TreeNode<RinkLayout, StyleModifier>,
|
||||
tree: &Tree<RinkLayout, StyleModifier>,
|
||||
) {
|
||||
// only trigger event if the event was not triggered already by a child
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
if events.insert(name) {
|
||||
resolved_events.push(UserEvent {
|
||||
scope_id: None,
|
||||
priority: EventPriority::Medium,
|
||||
name,
|
||||
element: Some(node.id),
|
||||
data: Arc::new(clone_mouse_data(data.mouse_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!(),
|
||||
resolved_events.push(UserEvent {
|
||||
scope_id: None,
|
||||
priority: EventPriority::Medium,
|
||||
name,
|
||||
element: Some(node.id),
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
let new_pos = (mouse.0.screen_x, mouse.0.screen_y);
|
||||
let old_pos = previous_mouse
|
||||
@@ -338,12 +256,245 @@ impl InnerInputState {
|
||||
mouse_data,
|
||||
wheel_data,
|
||||
};
|
||||
get_mouse_events(dom, resolved_events, layout, layouts, node, &data);
|
||||
}
|
||||
|
||||
// for s in &self.subscribers {
|
||||
// s();
|
||||
// }
|
||||
{
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// 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>) {
|
||||
@@ -364,7 +515,7 @@ impl RinkInputHandler {
|
||||
cx: &ScopeState,
|
||||
) -> (Self, Rc<RefCell<InnerInputState>>) {
|
||||
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 {
|
||||
while let Some(evt) = receiver.next().await {
|
||||
@@ -391,68 +542,63 @@ impl RinkInputHandler {
|
||||
|
||||
pub fn get_events<'a>(
|
||||
&self,
|
||||
dom: &'a VirtualDom,
|
||||
layout: &Stretch,
|
||||
layouts: &mut Tree<RinkLayout, StyleModifier>,
|
||||
node: &'a VNode<'a>,
|
||||
tree: &mut Tree<RinkLayout, StyleModifier>,
|
||||
) -> Vec<UserEvent> {
|
||||
// todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
|
||||
fn inner(
|
||||
queue: &[(&'static str, Arc<dyn Any + Send + Sync>)],
|
||||
resolved: &mut Vec<UserEvent>,
|
||||
node: &VNode,
|
||||
) {
|
||||
match node {
|
||||
VNode::Fragment(frag) => {
|
||||
for c in frag.children {
|
||||
inner(queue, resolved, c);
|
||||
}
|
||||
}
|
||||
VNode::Element(el) => {
|
||||
for l in el.listeners {
|
||||
for (name, data) in queue.iter() {
|
||||
if *name == l.event {
|
||||
if let Some(id) = el.id.get() {
|
||||
resolved.push(UserEvent {
|
||||
scope_id: None,
|
||||
priority: EventPriority::Medium,
|
||||
name: *name,
|
||||
element: Some(id),
|
||||
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,
|
||||
tree,
|
||||
);
|
||||
|
||||
let events: Vec<_> = self
|
||||
let events = 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();
|
||||
.filter(|e| {
|
||||
![
|
||||
"mouseenter",
|
||||
"mouseover",
|
||||
"mouseleave",
|
||||
"mouseout",
|
||||
"mousedown",
|
||||
"mouseup",
|
||||
"mousemove",
|
||||
"drag",
|
||||
"wheel",
|
||||
"click",
|
||||
"contextmenu",
|
||||
]
|
||||
.contains(&e.0)
|
||||
})
|
||||
.map(|evt| (evt.0, evt.1.into_any()));
|
||||
|
||||
inner(&events, &mut resolved_events, node);
|
||||
// todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
|
||||
let mut hm: HashMap<&'static str, Vec<Arc<dyn Any + Send + Sync>>> = HashMap::new();
|
||||
for (event, data) in events {
|
||||
if let Some(v) = hm.get_mut(event) {
|
||||
v.push(data);
|
||||
} else {
|
||||
hm.insert(event, vec![data]);
|
||||
}
|
||||
}
|
||||
for (event, datas) in hm {
|
||||
for node in tree.get_listening_sorted(event) {
|
||||
for data in &datas {
|
||||
resolved_events.push(UserEvent {
|
||||
scope_id: None,
|
||||
priority: EventPriority::Medium,
|
||||
name: event,
|
||||
element: Some(node.id),
|
||||
data: data.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolved_events
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use dioxus_core::*;
|
||||
use dioxus_native_core::BubbledUpState;
|
||||
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.
|
||||
@@ -42,7 +42,6 @@ impl BubbledUpState for RinkLayout {
|
||||
|
||||
if let Some(n) = self.node {
|
||||
if self.style != style {
|
||||
panic!("new style: {style:?}");
|
||||
stretch.set_style(n, style).unwrap();
|
||||
}
|
||||
} else {
|
||||
@@ -74,11 +73,9 @@ impl BubbledUpState for RinkLayout {
|
||||
|
||||
if let Some(n) = self.node {
|
||||
if &stretch.children(n).unwrap() != &child_layout {
|
||||
panic!("new children: {child_layout:?}");
|
||||
stretch.set_children(n, &child_layout).unwrap();
|
||||
}
|
||||
if self.style != style {
|
||||
panic!("new style: {style:?}");
|
||||
stretch.set_style(n, style).unwrap();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,643 +0,0 @@
|
||||
/*
|
||||
- [ ] pub display: Display,
|
||||
- [x] pub position_type: PositionType, --> kinda, stretch doesnt support everything
|
||||
- [ ] pub direction: Direction,
|
||||
|
||||
- [x] pub flex_direction: FlexDirection,
|
||||
- [x] pub flex_wrap: FlexWrap,
|
||||
- [x] pub flex_grow: f32,
|
||||
- [x] pub flex_shrink: f32,
|
||||
- [x] pub flex_basis: Dimension,
|
||||
|
||||
- [x] pub overflow: Overflow, ---> kinda implemented... stretch doesnt have support for directional overflow
|
||||
|
||||
- [x] pub align_items: AlignItems,
|
||||
- [x] pub align_self: AlignSelf,
|
||||
- [x] pub align_content: AlignContent,
|
||||
|
||||
- [x] pub margin: Rect<Dimension>,
|
||||
- [x] pub padding: Rect<Dimension>,
|
||||
|
||||
- [x] pub justify_content: JustifyContent,
|
||||
- [ ] pub position: Rect<Dimension>,
|
||||
- [x] pub border: Rect<Dimension>,
|
||||
|
||||
- [ ] pub size: Size<Dimension>, ----> ??? seems to only be relevant for input?
|
||||
- [ ] pub min_size: Size<Dimension>,
|
||||
- [ ] pub max_size: Size<Dimension>,
|
||||
|
||||
- [ ] pub aspect_ratio: Number,
|
||||
*/
|
||||
|
||||
use stretch2::{prelude::*, style::PositionType, style::Style};
|
||||
|
||||
/// applies the entire html namespace defined in dioxus-html
|
||||
pub fn apply_layout_attributes(
|
||||
//
|
||||
name: &str,
|
||||
value: &str,
|
||||
style: &mut Style,
|
||||
) {
|
||||
match name {
|
||||
"align-content"
|
||||
| "align-items"
|
||||
| "align-self" => apply_align(name, value, style),
|
||||
|
||||
"animation"
|
||||
| "animation-delay"
|
||||
| "animation-direction"
|
||||
| "animation-duration"
|
||||
| "animation-fill-mode"
|
||||
| "animation-iteration-count"
|
||||
| "animation-name"
|
||||
| "animation-play-state"
|
||||
| "animation-timing-function" => apply_animation(name, value, style),
|
||||
|
||||
"backface-visibility" => {}
|
||||
|
||||
"border"
|
||||
| "border-bottom"
|
||||
| "border-bottom-color"
|
||||
| "border-bottom-left-radius"
|
||||
| "border-bottom-right-radius"
|
||||
| "border-bottom-style"
|
||||
| "border-bottom-width"
|
||||
| "border-collapse"
|
||||
| "border-color"
|
||||
| "border-image"
|
||||
| "border-image-outset"
|
||||
| "border-image-repeat"
|
||||
| "border-image-slice"
|
||||
| "border-image-source"
|
||||
| "border-image-width"
|
||||
| "border-left"
|
||||
| "border-left-color"
|
||||
| "border-left-style"
|
||||
| "border-left-width"
|
||||
| "border-radius"
|
||||
| "border-right"
|
||||
| "border-right-color"
|
||||
| "border-right-style"
|
||||
| "border-right-width"
|
||||
| "border-spacing"
|
||||
| "border-style"
|
||||
| "border-top"
|
||||
| "border-top-color"
|
||||
| "border-top-left-radius"
|
||||
| "border-top-right-radius"
|
||||
| "border-top-style"
|
||||
| "border-top-width"
|
||||
| "border-width" => apply_border(name, value, style),
|
||||
|
||||
"bottom" => {}
|
||||
"box-shadow" => {}
|
||||
"box-sizing" => {}
|
||||
"caption-side" => {}
|
||||
"clear" => {}
|
||||
"clip" => {}
|
||||
|
||||
"column-count"
|
||||
| "column-fill"
|
||||
| "column-gap"
|
||||
| "column-rule"
|
||||
| "column-rule-color"
|
||||
| "column-rule-style"
|
||||
| "column-rule-width"
|
||||
| "column-span"
|
||||
// add column-width
|
||||
| "column-width" => apply_column(name, value, style),
|
||||
|
||||
"columns" => {}
|
||||
|
||||
"content" => {}
|
||||
"counter-increment" => {}
|
||||
"counter-reset" => {}
|
||||
|
||||
"cursor" => {}
|
||||
"direction" => {
|
||||
match value {
|
||||
"ltr" => style.direction = Direction::LTR,
|
||||
"rtl" => style.direction = Direction::RTL,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
"display" => apply_display(name, value, style),
|
||||
|
||||
"empty-cells" => {}
|
||||
|
||||
"flex"
|
||||
| "flex-basis"
|
||||
| "flex-direction"
|
||||
| "flex-flow"
|
||||
| "flex-grow"
|
||||
| "flex-shrink"
|
||||
| "flex-wrap" => apply_flex(name, value, style),
|
||||
|
||||
"float" => {}
|
||||
|
||||
"height" => {
|
||||
if let Some(v) = parse_value(value){
|
||||
style.size.height = match v {
|
||||
UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
|
||||
UnitSystem::Point(v)=> Dimension::Points(v),
|
||||
};
|
||||
}
|
||||
}
|
||||
"justify-content" => {
|
||||
use JustifyContent::*;
|
||||
style.justify_content = match value {
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"space-between" => SpaceBetween,
|
||||
"space-around" => SpaceAround,
|
||||
"space-evenly" => SpaceEvenly,
|
||||
_ => FlexStart,
|
||||
};
|
||||
}
|
||||
"left" => {}
|
||||
"letter-spacing" => {}
|
||||
"line-height" => {}
|
||||
|
||||
"list-style"
|
||||
| "list-style-image"
|
||||
| "list-style-position"
|
||||
| "list-style-type" => {}
|
||||
|
||||
"margin"
|
||||
| "margin-bottom"
|
||||
| "margin-left"
|
||||
| "margin-right"
|
||||
| "margin-top" => apply_margin(name, value, style),
|
||||
|
||||
"max-height" => {}
|
||||
"max-width" => {}
|
||||
"min-height" => {}
|
||||
"min-width" => {}
|
||||
|
||||
"opacity" => {}
|
||||
"order" => {}
|
||||
"outline" => {}
|
||||
|
||||
"outline-color"
|
||||
| "outline-offset"
|
||||
| "outline-style"
|
||||
| "outline-width" => {}
|
||||
|
||||
"overflow"
|
||||
| "overflow-x"
|
||||
| "overflow-y" => apply_overflow(name, value, style),
|
||||
|
||||
"padding"
|
||||
| "padding-bottom"
|
||||
| "padding-left"
|
||||
| "padding-right"
|
||||
| "padding-top" => apply_padding(name, value, style),
|
||||
|
||||
"page-break-after"
|
||||
| "page-break-before"
|
||||
| "page-break-inside" => {}
|
||||
|
||||
"perspective"
|
||||
| "perspective-origin" => {}
|
||||
|
||||
"position" => {
|
||||
match value {
|
||||
"static" => {}
|
||||
"relative" => style.position_type = PositionType::Relative,
|
||||
"fixed" => {}
|
||||
"absolute" => style.position_type = PositionType::Absolute,
|
||||
"sticky" => {}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"pointer-events" => {}
|
||||
|
||||
"quotes" => {}
|
||||
"resize" => {}
|
||||
"right" => {}
|
||||
"tab-size" => {}
|
||||
"table-layout" => {}
|
||||
|
||||
"top" => {}
|
||||
|
||||
"transform"
|
||||
| "transform-origin"
|
||||
| "transform-style" => apply_transform(name, value, style),
|
||||
|
||||
"transition"
|
||||
| "transition-delay"
|
||||
| "transition-duration"
|
||||
| "transition-property"
|
||||
| "transition-timing-function" => apply_transition(name, value, style),
|
||||
|
||||
"vertical-align" => {}
|
||||
"visibility" => {}
|
||||
"white-space" => {}
|
||||
"width" => {
|
||||
if let Some(v) = parse_value(value){
|
||||
style.size.width = match v {
|
||||
UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
|
||||
UnitSystem::Point(v)=> Dimension::Points(v),
|
||||
};
|
||||
}
|
||||
}
|
||||
"word-break" => {}
|
||||
"word-spacing" => {}
|
||||
"word-wrap" => {}
|
||||
"z-index" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum UnitSystem {
|
||||
Percent(f32),
|
||||
Point(f32),
|
||||
}
|
||||
|
||||
impl Into<Dimension> for UnitSystem {
|
||||
fn into(self) -> Dimension {
|
||||
match self {
|
||||
Self::Percent(v) => Dimension::Percent(v),
|
||||
Self::Point(v) => Dimension::Points(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_value(value: &str) -> Option<UnitSystem> {
|
||||
if value.ends_with("px") {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
Some(UnitSystem::Point(px))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if value.ends_with('%') {
|
||||
if let Ok(pct) = value.trim_end_matches('%').parse::<f32>() {
|
||||
Some(UnitSystem::Percent(pct))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_overflow(name: &str, value: &str, style: &mut Style) {
|
||||
match name {
|
||||
// todo: add more overflow support to stretch2
|
||||
"overflow" | "overflow-x" | "overflow-y" => {
|
||||
style.overflow = match value {
|
||||
"auto" => Overflow::Visible,
|
||||
"hidden" => Overflow::Hidden,
|
||||
"scroll" => Overflow::Scroll,
|
||||
"visible" => Overflow::Visible,
|
||||
_ => Overflow::Visible,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_display(_name: &str, value: &str, style: &mut Style) {
|
||||
style.display = match value {
|
||||
"flex" => Display::Flex,
|
||||
"block" => Display::None,
|
||||
_ => Display::Flex,
|
||||
}
|
||||
|
||||
// TODO: there are way more variants
|
||||
// stretch needs to be updated to handle them
|
||||
//
|
||||
// "block" => Display::Block,
|
||||
// "inline" => Display::Inline,
|
||||
// "inline-block" => Display::InlineBlock,
|
||||
// "inline-table" => Display::InlineTable,
|
||||
// "list-item" => Display::ListItem,
|
||||
// "run-in" => Display::RunIn,
|
||||
// "table" => Display::Table,
|
||||
// "table-caption" => Display::TableCaption,
|
||||
// "table-cell" => Display::TableCell,
|
||||
// "table-column" => Display::TableColumn,
|
||||
// "table-column-group" => Display::TableColumnGroup,
|
||||
// "table-footer-group" => Display::TableFooterGroup,
|
||||
// "table-header-group" => Display::TableHeaderGroup,
|
||||
// "table-row" => Display::TableRow,
|
||||
// "table-row-group" => Display::TableRowGroup,
|
||||
// "none" => Display::None,
|
||||
// _ => Display::Inline,
|
||||
}
|
||||
|
||||
fn apply_border(name: &str, value: &str, style: &mut Style) {
|
||||
match name {
|
||||
"border" => {}
|
||||
"border-bottom" => {}
|
||||
"border-bottom-color" => {}
|
||||
"border-bottom-left-radius" => {}
|
||||
"border-bottom-right-radius" => {}
|
||||
"border-bottom-style" => {
|
||||
if style.border.bottom == Dimension::default() {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.bottom = v;
|
||||
}
|
||||
}
|
||||
"border-bottom-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.border.bottom = v.into();
|
||||
}
|
||||
}
|
||||
"border-collapse" => {}
|
||||
"border-color" => {}
|
||||
"border-image" => {}
|
||||
"border-image-outset" => {}
|
||||
"border-image-repeat" => {}
|
||||
"border-image-slice" => {}
|
||||
"border-image-source" => {}
|
||||
"border-image-width" => {}
|
||||
"border-left" => {}
|
||||
"border-left-color" => {}
|
||||
"border-left-style" => {
|
||||
if style.border.start == Dimension::default() {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.start = v;
|
||||
}
|
||||
}
|
||||
"border-left-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.border.start = v.into();
|
||||
}
|
||||
}
|
||||
"border-radius" => {}
|
||||
"border-right" => {}
|
||||
"border-right-color" => {}
|
||||
"border-right-style" => {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.end = v;
|
||||
}
|
||||
"border-right-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.border.end = v.into();
|
||||
}
|
||||
}
|
||||
"border-spacing" => {}
|
||||
"border-style" => {
|
||||
if style.border.top == Dimension::default() {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.top = v;
|
||||
}
|
||||
if style.border.bottom == Dimension::default() {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.bottom = v;
|
||||
}
|
||||
if style.border.start == Dimension::default() {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.start = v;
|
||||
}
|
||||
if style.border.end == Dimension::default() {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.end = v;
|
||||
}
|
||||
}
|
||||
"border-top" => {}
|
||||
"border-top-color" => {}
|
||||
"border-top-left-radius" => {}
|
||||
"border-top-right-radius" => {}
|
||||
"border-top-style" => {
|
||||
if style.border.top == Dimension::default() {
|
||||
let v = Dimension::Points(1.0);
|
||||
style.border.top = v;
|
||||
}
|
||||
}
|
||||
"border-top-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.border.top = v.into();
|
||||
}
|
||||
}
|
||||
"border-width" => {
|
||||
let values: Vec<_> = value.split(' ').collect();
|
||||
if values.len() == 1 {
|
||||
if let Some(w) = parse_value(values[0]) {
|
||||
style.border.top = w.into();
|
||||
style.border.bottom = w.into();
|
||||
style.border.start = w.into();
|
||||
style.border.end = w.into();
|
||||
}
|
||||
} else {
|
||||
let border_widths = [
|
||||
&mut style.border.top,
|
||||
&mut style.border.bottom,
|
||||
&mut style.border.start,
|
||||
&mut style.border.end,
|
||||
];
|
||||
for (v, width) in values.into_iter().zip(border_widths) {
|
||||
if let Some(w) = parse_value(v) {
|
||||
*width = w.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_animation(name: &str, _value: &str, _style: &mut Style) {
|
||||
match name {
|
||||
"animation" => {}
|
||||
"animation-delay" => {}
|
||||
"animation-direction =>{}" => {}
|
||||
"animation-duration" => {}
|
||||
"animation-fill-mode" => {}
|
||||
"animation-itera =>{}tion-count" => {}
|
||||
"animation-name" => {}
|
||||
"animation-play-state" => {}
|
||||
"animation-timing-function" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_column(name: &str, _value: &str, _style: &mut Style) {
|
||||
match name {
|
||||
"column-count" => {}
|
||||
"column-fill" => {}
|
||||
"column-gap" => {}
|
||||
"column-rule" => {}
|
||||
"column-rule-color" => {}
|
||||
"column-rule-style" => {}
|
||||
"column-rule-width" => {}
|
||||
"column-span" => {}
|
||||
"column-width" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_flex(name: &str, value: &str, style: &mut Style) {
|
||||
// - [x] pub flex_direction: FlexDirection,
|
||||
// - [x] pub flex_wrap: FlexWrap,
|
||||
// - [x] pub flex_grow: f32,
|
||||
// - [x] pub flex_shrink: f32,
|
||||
// - [x] pub flex_basis: Dimension,
|
||||
|
||||
match name {
|
||||
"flex" => {}
|
||||
"flex-direction" => {
|
||||
use FlexDirection::*;
|
||||
style.flex_direction = match value {
|
||||
"row" => Row,
|
||||
"row-reverse" => RowReverse,
|
||||
"column" => Column,
|
||||
"column-reverse" => ColumnReverse,
|
||||
_ => Row,
|
||||
};
|
||||
}
|
||||
"flex-basis" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.flex_basis = match v {
|
||||
UnitSystem::Percent(v) => Dimension::Percent(v / 100.0),
|
||||
UnitSystem::Point(v) => Dimension::Points(v),
|
||||
};
|
||||
}
|
||||
}
|
||||
"flex-flow" => {}
|
||||
"flex-grow" => {
|
||||
if let Ok(val) = value.parse::<f32>() {
|
||||
style.flex_grow = val;
|
||||
}
|
||||
}
|
||||
"flex-shrink" => {
|
||||
if let Ok(px) = value.parse::<f32>() {
|
||||
style.flex_shrink = px;
|
||||
}
|
||||
}
|
||||
"flex-wrap" => {
|
||||
use FlexWrap::*;
|
||||
style.flex_wrap = match value {
|
||||
"nowrap" => NoWrap,
|
||||
"wrap" => Wrap,
|
||||
"wrap-reverse" => WrapReverse,
|
||||
_ => NoWrap,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_padding(name: &str, value: &str, style: &mut Style) {
|
||||
match parse_value(value) {
|
||||
Some(UnitSystem::Percent(v)) => match name {
|
||||
"padding" => {
|
||||
let v = Dimension::Percent(v / 100.0);
|
||||
style.padding.top = v;
|
||||
style.padding.bottom = v;
|
||||
style.padding.start = v;
|
||||
style.padding.end = v;
|
||||
}
|
||||
"padding-bottom" => style.padding.bottom = Dimension::Percent(v / 100.0),
|
||||
"padding-left" => style.padding.start = Dimension::Percent(v / 100.0),
|
||||
"padding-right" => style.padding.end = Dimension::Percent(v / 100.0),
|
||||
"padding-top" => style.padding.top = Dimension::Percent(v / 100.0),
|
||||
_ => {}
|
||||
},
|
||||
Some(UnitSystem::Point(v)) => match name {
|
||||
"padding" => {
|
||||
style.padding.top = Dimension::Points(v);
|
||||
style.padding.bottom = Dimension::Points(v);
|
||||
style.padding.start = Dimension::Points(v);
|
||||
style.padding.end = Dimension::Points(v);
|
||||
}
|
||||
"padding-bottom" => style.padding.bottom = Dimension::Points(v),
|
||||
"padding-left" => style.padding.start = Dimension::Points(v),
|
||||
"padding-right" => style.padding.end = Dimension::Points(v),
|
||||
"padding-top" => style.padding.top = Dimension::Points(v),
|
||||
_ => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_transform(_name: &str, _value: &str, _style: &mut Style) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_transition(_name: &str, _value: &str, _style: &mut Style) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_align(name: &str, value: &str, style: &mut Style) {
|
||||
match name {
|
||||
"align-items" => {
|
||||
use AlignItems::*;
|
||||
style.align_items = match value {
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"baseline" => Baseline,
|
||||
"stretch" => Stretch,
|
||||
_ => FlexStart,
|
||||
};
|
||||
}
|
||||
"align-content" => {
|
||||
use AlignContent::*;
|
||||
style.align_content = match value {
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"space-between" => SpaceBetween,
|
||||
"space-around" => SpaceAround,
|
||||
_ => FlexStart,
|
||||
};
|
||||
}
|
||||
"align-self" => {
|
||||
use AlignSelf::*;
|
||||
style.align_self = match value {
|
||||
"auto" => Auto,
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"baseline" => Baseline,
|
||||
"stretch" => Stretch,
|
||||
_ => Auto,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_size(_name: &str, _value: &str, _style: &mut Style) {
|
||||
//
|
||||
}
|
||||
|
||||
pub fn apply_margin(name: &str, value: &str, style: &mut Style) {
|
||||
match parse_value(value) {
|
||||
Some(UnitSystem::Percent(v)) => match name {
|
||||
"margin" => {
|
||||
let v = Dimension::Percent(v / 100.0);
|
||||
style.margin.top = v;
|
||||
style.margin.bottom = v;
|
||||
style.margin.start = v;
|
||||
style.margin.end = v;
|
||||
}
|
||||
"margin-top" => style.margin.top = Dimension::Percent(v / 100.0),
|
||||
"margin-bottom" => style.margin.bottom = Dimension::Percent(v / 100.0),
|
||||
"margin-left" => style.margin.start = Dimension::Percent(v / 100.0),
|
||||
"margin-right" => style.margin.end = Dimension::Percent(v / 100.0),
|
||||
_ => {}
|
||||
},
|
||||
Some(UnitSystem::Point(v)) => match name {
|
||||
"margin" => {
|
||||
style.margin.top = Dimension::Points(v);
|
||||
style.margin.bottom = Dimension::Points(v);
|
||||
style.margin.start = Dimension::Points(v);
|
||||
style.margin.end = Dimension::Points(v);
|
||||
}
|
||||
"margin-top" => style.margin.top = Dimension::Points(v),
|
||||
"margin-bottom" => style.margin.bottom = Dimension::Points(v),
|
||||
"margin-left" => style.margin.start = Dimension::Points(v),
|
||||
"margin-right" => style.margin.end = Dimension::Points(v),
|
||||
_ => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
+59
-57
@@ -1,3 +1,6 @@
|
||||
// notes:
|
||||
// mouse events binary search was broken for absolutely positioned elements
|
||||
|
||||
use anyhow::Result;
|
||||
use crossterm::{
|
||||
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::*;
|
||||
use dioxus_html::on::{KeyboardData, MouseData, PointerData, TouchData, WheelData};
|
||||
use dioxus_native_core::Tree;
|
||||
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt};
|
||||
use std::{
|
||||
io,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use stretch2::{
|
||||
prelude::{Layout, Size},
|
||||
Stretch,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use std::{io, time::Duration};
|
||||
use stretch2::{prelude::Size, Stretch};
|
||||
use style_attributes::StyleModifier;
|
||||
use tokio::time::Instant;
|
||||
use tui::{backend::CrosstermBackend, Terminal};
|
||||
|
||||
mod config;
|
||||
mod hooks;
|
||||
mod layout;
|
||||
mod layout_attributes;
|
||||
mod render;
|
||||
mod style;
|
||||
mod style_attributes;
|
||||
@@ -31,7 +30,6 @@ mod widget;
|
||||
pub use config::*;
|
||||
pub use hooks::*;
|
||||
pub use layout::*;
|
||||
pub use layout_attributes::*;
|
||||
pub use render::*;
|
||||
|
||||
pub fn launch(app: Component<()>) {
|
||||
@@ -104,6 +102,12 @@ pub fn render_vdom(
|
||||
let mut terminal = Terminal::new(backend).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 {
|
||||
/*
|
||||
@@ -115,54 +119,51 @@ pub fn render_vdom(
|
||||
|
||||
use simd to compare lines for diffing?
|
||||
|
||||
|
||||
todo: reuse the layout and node objects.
|
||||
our work_with_deadline method can tell us which nodes are dirty.
|
||||
todo: lazy re-rendering
|
||||
*/
|
||||
|
||||
/*
|
||||
Compute the layout given the terminal size
|
||||
*/
|
||||
let mut events = Vec::new();
|
||||
if !to_rerender.is_empty() {
|
||||
terminal.draw(|frame| {
|
||||
// size is guaranteed to not change when rendering
|
||||
let dims = frame.size();
|
||||
// println!("{dims:?}");
|
||||
let width = dims.width;
|
||||
let height = dims.height;
|
||||
let root_id = tree.root;
|
||||
let root_node = tree.get(root_id).up_state.node.unwrap();
|
||||
stretch
|
||||
.compute_layout(
|
||||
root_node,
|
||||
Size {
|
||||
width: stretch2::prelude::Number::Defined((width - 1) as f32),
|
||||
height: stretch2::prelude::Number::Defined((height - 1) as f32),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let root = tree.get(tree.root);
|
||||
render::render_vnode(frame, &stretch, &tree, &root, cfg);
|
||||
})?;
|
||||
}
|
||||
|
||||
terminal.draw(|frame| {
|
||||
// size is guaranteed to not change when rendering
|
||||
let dims = frame.size();
|
||||
println!("{dims:?}");
|
||||
let width = dims.width;
|
||||
let height = dims.height;
|
||||
let root_id = tree.root;
|
||||
let root_node = tree.get(root_id).up_state.node.unwrap();
|
||||
stretch
|
||||
.compute_layout(
|
||||
root_node,
|
||||
Size {
|
||||
width: stretch2::prelude::Number::Defined((width - 1) as f32),
|
||||
height: stretch2::prelude::Number::Defined((height - 1) as f32),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// resolve events before rendering
|
||||
events = handler.get_events(
|
||||
vdom,
|
||||
&stretch,
|
||||
&mut tree,
|
||||
vdom.base_scope().root_node(),
|
||||
);
|
||||
for n in &tree.nodes {
|
||||
if let Some(node) = n {
|
||||
let Layout { location, size, .. } =
|
||||
stretch.layout(node.up_state.node.unwrap()).unwrap();
|
||||
println!("{node:#?}");
|
||||
println!("\t{location:?}: {size:?}");
|
||||
}
|
||||
}
|
||||
let root = tree.get(tree.root);
|
||||
// render::render_vnode(frame, &stretch, &tree, &root, cfg);
|
||||
})?;
|
||||
|
||||
for e in events {
|
||||
// resolve events before rendering
|
||||
// todo: events do not trigger update?
|
||||
for e in handler.get_events(&stretch, &mut tree) {
|
||||
let tname = if e.data.is::<PointerData>() {
|
||||
"PointerData"
|
||||
} else if e.data.is::<WheelData>() {
|
||||
"WheelData"
|
||||
} else if e.data.is::<MouseData>() {
|
||||
"MouseData"
|
||||
} else if e.data.is::<KeyboardData>() {
|
||||
"KeyboardData"
|
||||
} else if e.data.is::<TouchData>() {
|
||||
"TouchData"
|
||||
} else if e.data.is::<(u16, u16)>() {
|
||||
"(u16, u16)"
|
||||
} else {
|
||||
panic!()
|
||||
};
|
||||
// println!("{tname}: {e:?}");
|
||||
vdom.handle_message(SchedulerMsg::Event(e));
|
||||
}
|
||||
|
||||
@@ -199,8 +200,10 @@ pub fn render_vdom(
|
||||
}
|
||||
|
||||
let mutations = vdom.work_with_deadline(|| false);
|
||||
// updates the tree's nodes
|
||||
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 ())
|
||||
.unwrap();
|
||||
}
|
||||
@@ -220,7 +223,6 @@ pub fn render_vdom(
|
||||
enum InputEvent {
|
||||
UserInput(TermEvent),
|
||||
Tick,
|
||||
|
||||
#[allow(dead_code)]
|
||||
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 stretch2::{
|
||||
geometry::Point,
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
style::{RinkColor, RinkStyle},
|
||||
style_attributes::{BorderEdge, BorderStyle},
|
||||
widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
|
||||
Config, RinkLayout, StyleModifier, UnitSystem,
|
||||
Config, RinkLayout, StyleModifier,
|
||||
};
|
||||
|
||||
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
||||
@@ -48,7 +48,7 @@ pub fn render_vnode<'a>(
|
||||
// println!("{:?}", self.style);
|
||||
new_cell.set_style(self.style);
|
||||
new_cell.symbol = c.to_string();
|
||||
buf.set(area.left() + i as u16, area.top(), &new_cell);
|
||||
buf.set(area.left() + i as u16, area.top(), new_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
|
||||
buf.set(
|
||||
(current[0] + pos[0] 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 {
|
||||
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]) {
|
||||
new_cell.symbol = symbols.horizontal.to_string();
|
||||
buf.set(x, area.top(), &new_cell);
|
||||
buf.set(x, area.top(), new_cell.clone());
|
||||
}
|
||||
draw_arc(
|
||||
[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]) {
|
||||
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(
|
||||
[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) {
|
||||
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(
|
||||
[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) {
|
||||
new_cell.symbol = symbols.vertical.to_string();
|
||||
buf.set(area.left(), y, &new_cell);
|
||||
buf.set(area.left(), y, new_cell.clone());
|
||||
}
|
||||
draw_arc(
|
||||
[area.left() + radius[0], area.top() + radius[1]],
|
||||
|
||||
@@ -30,14 +30,13 @@
|
||||
*/
|
||||
|
||||
use dioxus_core::{Attribute, VNode};
|
||||
use dioxus_native_core::PushedDownState;
|
||||
|
||||
use crate::{
|
||||
parse_value,
|
||||
style::{RinkColor, RinkStyle},
|
||||
UnitSystem,
|
||||
use dioxus_native_core::{
|
||||
layout_attributes::{parse_value, UnitSystem},
|
||||
PushedDownState,
|
||||
};
|
||||
|
||||
use crate::style::{RinkColor, RinkStyle};
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Debug)]
|
||||
pub struct StyleModifier {
|
||||
pub style: RinkStyle,
|
||||
|
||||
@@ -20,7 +20,7 @@ impl<'a> RinkBuffer<'a> {
|
||||
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();
|
||||
if x < area.x || x > area.width || y < area.y || y > area.height {
|
||||
panic!("({x}, {y}) is not in {area:?}");
|
||||
@@ -34,7 +34,7 @@ impl<'a> RinkBuffer<'a> {
|
||||
}
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user