rebase master

This commit is contained in:
Evan Almloff committed 2022-03-26 20:27:10 -05:00
1 parent 499971e9b3
commit 26d92b6e51
14 files changed
+529 -318

No files matched your search

+329 -183
View File
@@ -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
}
+1 -4
View File
@@ -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 {
-643
View File
@@ -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
View File
@@ -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,
}
+9 -9
View File
@@ -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]],
+5 -6
View File
@@ -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,
+2 -2
View File
@@ -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));
}
}