Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion benches/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn compute_node_count(children_per_node: usize, depth: usize, node_count: &mut u
}
}

return *node_count;
*node_count
}

fn morphorm_benchmarks(c: &mut Criterion) {
Expand Down Expand Up @@ -124,6 +124,27 @@ fn morphorm_benchmarks(c: &mut Criterion) {
});

group.finish();

let mut group = c.benchmark_group("Steady State Tree");
group.sample_size(20);

let children_per_node = 10;
let depth = 6usize;
let mut world = World::default();
let root = build_tree(&mut world, None, children_per_node, depth);

let benchmark_label = format!(
"Steady state bench. {children_per_node} children per node, depth: {depth}. Total nodes: {}.",
compute_node_count(children_per_node, depth, &mut 0)
);

group.bench_function(benchmark_label, |b| {
b.iter(|| {
root.layout(&mut world.cache, &world.tree, &world.store, &mut ());
})
});

group.finish();
}

criterion_group!(benches, morphorm_benchmarks);
Expand Down
15 changes: 14 additions & 1 deletion ecs/src/implementations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ impl Node for Entity {
store.position_type.get(*self).copied()
}

fn direction(&self, store: &Store) -> Option<Direction> {
store.direction.get(*self).copied()
}

fn wrap(&self, store: &Store) -> Option<LayoutWrap> {
store.wrap.get(*self).copied()
}

fn alignment(&self, store: &Store) -> Option<Alignment> {
store.alignment.get(*self).copied()
}
Expand Down Expand Up @@ -186,7 +194,6 @@ pub struct Rect {
pub height: f32,
}

#[derive(Default, Debug)]
pub struct NodeCache {
// Computed size and position of nodes.
pub rect: SecondaryMap<Entity, Rect>,
Expand Down Expand Up @@ -254,3 +261,9 @@ impl Cache for NodeCache {
0.0
}
}

impl Default for NodeCache {
fn default() -> Self {
Self { rect: SecondaryMap::new() }
}
}
8 changes: 7 additions & 1 deletion ecs/src/store.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Part of a very simple ECS for demonstration purposes only.

use crate::{entity::Entity, TextWrap};
use morphorm::{LayoutType, PositionType, Units, Alignment};
use morphorm::{Alignment, Direction, LayoutType, LayoutWrap, PositionType, Units};
use slotmap::SecondaryMap;

type ContentSizeType<S> = Box<dyn Fn(&S, Option<f32>, Option<f32>) -> (f32, f32)>;
Expand All @@ -13,7 +13,9 @@ pub struct Store {

pub layout_type: SecondaryMap<Entity, LayoutType>,
pub position_type: SecondaryMap<Entity, PositionType>,
pub direction: SecondaryMap<Entity, Direction>,
pub alignment: SecondaryMap<Entity, Alignment>,
pub wrap: SecondaryMap<Entity, LayoutWrap>,

pub grid_columns: SecondaryMap<Entity, Vec<Units>>,
pub grid_rows: SecondaryMap<Entity, Vec<Units>>,
Expand Down Expand Up @@ -72,6 +74,8 @@ impl Store {
self.visible.remove(entity);
self.layout_type.remove(entity);
self.position_type.remove(entity);
self.direction.remove(entity);
self.wrap.remove(entity);
self.left.remove(entity);
self.right.remove(entity);
self.top.remove(entity);
Expand Down Expand Up @@ -110,6 +114,8 @@ impl Store {
self.visible.clear();
self.layout_type.clear();
self.position_type.clear();
self.direction.clear();
Comment thread
geom3trik marked this conversation as resolved.
self.wrap.clear();
self.left.clear();
self.right.clear();
self.top.clear();
Expand Down
22 changes: 15 additions & 7 deletions ecs/src/world.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Part of a very simple ECS for demonstration purposes only.

use morphorm::{LayoutType, PositionType, Units, Alignment};
use morphorm::{Alignment, Direction, LayoutType, LayoutWrap, PositionType, Units};

use crate::entity::{Entity, EntityManager};
use crate::implementations::NodeCache;
Expand Down Expand Up @@ -68,6 +68,16 @@ impl World {
self.store.position_type.insert(entity, value);
}

/// Set the inline direction used for horizontal positioning semantics for the given entity.
pub fn set_direction(&mut self, entity: Entity, value: Direction) {
self.store.direction.insert(entity, value);
}

/// Set the wrap mode for children of the given entity.
pub fn set_wrap(&mut self, entity: Entity, value: LayoutWrap) {
self.store.wrap.insert(entity, value);
}

pub fn set_alignment(&mut self, entity: Entity, value: Alignment) {
self.store.alignment.insert(entity, value);
}
Expand Down Expand Up @@ -183,34 +193,32 @@ impl World {
pub fn set_vertical_scroll(&mut self, entity: Entity, value: f32) {
self.store.vertical_scroll.insert(entity, value);
}

/// Set the desired horizontal scroll offset.
pub fn set_horizontal_scroll(&mut self, entity: Entity, value: f32) {
self.store.horizontal_scroll.insert(entity, value);
}


pub fn set_grid_columns(&mut self, entity: Entity, value: Vec<Units>) {
self.store.grid_columns.insert(entity, value);
}



pub fn set_grid_rows(&mut self, entity: Entity, value: Vec<Units>) {
self.store.grid_rows.insert(entity, value);
}

pub fn set_column_start(&mut self, entity: Entity, value: usize) {
self.store.column_start.insert(entity, value);
}

pub fn set_row_start(&mut self, entity: Entity, value: usize) {
self.store.row_start.insert(entity, value);
}

pub fn set_column_span(&mut self, entity: Entity, value: usize) {
self.store.column_span.insert(entity, value);
}

pub fn set_row_span(&mut self, entity: Entity, value: usize) {
self.store.row_span.insert(entity, value);
}
Expand Down
22 changes: 11 additions & 11 deletions examples/advanced.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ pub fn render(mut cache: LayoutCache, mut root: Widget) {
*control_flow = ControlFlow::Wait;

match event {
Event::LoopDestroyed => return,
Event::LoopDestroyed => (),
Event::WindowEvent { ref event, .. } => match event {
WindowEvent::Resized(size) => {
surface.resize(&context, size.width.try_into().unwrap(), size.height.try_into().unwrap());
Expand All @@ -386,19 +386,19 @@ pub fn render(mut cache: LayoutCache, mut root: Widget) {
}
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,

WindowEvent::KeyboardInput { device_id: _, input, is_synthetic: _ } => {
if input.virtual_keycode == Some(VirtualKeyCode::H) && input.state == ElementState::Pressed {
print_node(&root, &cache, &(), true, false, String::new());
}
WindowEvent::KeyboardInput { device_id: _, input, is_synthetic: _ }
if input.virtual_keycode == Some(VirtualKeyCode::H) && input.state == ElementState::Pressed =>
{
print_node(&root, &cache, &(), true, false, String::new());
}
_ => (),
},
Event::RedrawRequested(_) => {
let dpi_factor = window.scale_factor();
let size = window.inner_size();

canvas.set_size(size.width as u32, size.height as u32, dpi_factor as f32);
canvas.clear_rect(0, 0, size.width as u32, size.height as u32, Color::rgbf(0.3, 0.3, 0.32));
canvas.set_size(size.width, size.height, dpi_factor as f32);
canvas.clear_rect(0, 0, size.width, size.height, Color::rgbf(0.3, 0.3, 0.32));

draw_node(&root, &cache, &mut canvas, font);

Expand All @@ -422,16 +422,16 @@ fn draw_node(node: &Widget, cache: &LayoutCache, canvas: &mut Canvas<OpenGl>, fo
let mut path = Path::new();
path.rect(posx, posy, width, height);
let paint = Paint::color(color);
canvas.fill_path(&mut path, &paint);
canvas.fill_path(&path, &paint);

let mut paint = Paint::color(Color::black());
paint.set_font_size(24.0);
paint.set_text_align(Align::Center);
paint.set_text_baseline(Baseline::Middle);
paint.set_font(&vec![font]);
let _ = canvas.fill_text(posx + width / 2.0, posy + height / 2.0, &node.key().to_string(), &paint);
paint.set_font(&[font]);
let _ = canvas.fill_text(posx + width / 2.0, posy + height / 2.0, node.key().to_string(), &paint);

for child in (&node).children(&()) {
for child in node.children(&()) {
draw_node(child, cache, canvas, font);
}
}
30 changes: 15 additions & 15 deletions examples/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub fn render(mut world: World, root: Entity) {
*control_flow = ControlFlow::Wait;

match event {
Event::LoopDestroyed => return,
Event::LoopDestroyed => (),
Event::WindowEvent { ref event, .. } => match event {
WindowEvent::Resized(size) => {
surface.resize(&context, size.width.try_into().unwrap(), size.height.try_into().unwrap());
Expand All @@ -126,20 +126,20 @@ pub fn render(mut world: World, root: Entity) {
}
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,

WindowEvent::KeyboardInput { device_id: _, input, is_synthetic: _ } => {
if input.virtual_keycode == Some(VirtualKeyCode::H) && input.state == ElementState::Pressed {
print_node(&root, &world.cache, &world.tree, true, false, String::new());
}
WindowEvent::KeyboardInput { device_id: _, input, is_synthetic: _ }
if input.virtual_keycode == Some(VirtualKeyCode::H) && input.state == ElementState::Pressed =>
{
print_node(&root, &world.cache, &world.tree, true, false, String::new());
}
_ => (),
},
Event::RedrawRequested(_) => {
let size = window.inner_size();

canvas.set_size(size.width as u32, size.height as u32, 1.0);
canvas.clear_rect(0, 0, size.width as u32, size.height as u32, Color::rgbf(0.3, 0.3, 0.32));
canvas.set_size(size.width, size.height, 1.0);
canvas.clear_rect(0, 0, size.width, size.height, Color::rgbf(0.3, 0.3, 0.32));

draw_node(&root, &world.tree, &world.cache, &world.store, 0.0, 0.0, font, &mut canvas);
draw_node(&root, &world.tree, &world.cache, &world.store, (0.0, 0.0), font, &mut canvas);

canvas.flush();
surface.swap_buffers(&context).unwrap();
Expand All @@ -157,11 +157,11 @@ fn draw_node<N: Node<CacheKey = Entity>>(
tree: &N::Tree,
cache: &impl Cache<Node = N>,
store: &Store,
parent_posx: f32,
parent_posy: f32,
parent_pos: (f32, f32),
font: FontId,
canvas: &mut Canvas<OpenGl>,
) {
let (parent_posx, parent_posy) = parent_pos;
let posx = cache.posx(node);
let posy = cache.posy(node);
let width = cache.width(node);
Expand All @@ -174,14 +174,14 @@ fn draw_node<N: Node<CacheKey = Entity>>(
let mut path = Path::new();
path.rect(parent_posx + posx, parent_posy + posy, width, height);
let paint = Paint::color(Color::rgb(*red, *green, *blue));
canvas.fill_path(&mut path, &paint);
canvas.fill_path(&path, &paint);

if let Some(text) = store.text.get(node.key()) {
let mut paint = Paint::color(Color::black());
paint.set_font_size(48.0);
paint.set_text_align(Align::Left);
paint.set_text_baseline(Baseline::Top);
paint.set_font(&vec![font]);
paint.set_font(&[font]);

let font_metrics = canvas.measure_font(&paint).expect("Error measuring font");

Expand All @@ -199,17 +199,17 @@ fn draw_node<N: Node<CacheKey = Entity>>(
paint.set_font_size(48.0);
paint.set_text_align(Align::Center);
paint.set_text_baseline(Baseline::Middle);
paint.set_font(&vec![font]);
paint.set_font(&[font]);

let _ = canvas.fill_text(
parent_posx + posx + width / 2.0,
parent_posy + posy + height / 2.0,
&node.key().0.to_string(),
node.key().0.to_string(),
&paint,
);
}

for child in node.children(tree) {
draw_node(child, tree, cache, store, posx + parent_posx, posy + parent_posy, font, canvas);
draw_node(child, tree, cache, store, (posx + parent_posx, posy + parent_posy), font, canvas);
}
}
2 changes: 1 addition & 1 deletion examples/textwrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn content_size(node: Entity, store: &Store, width: Option<f32>, height: Option<
paint.set_font_size(48.0);
paint.set_text_align(femtovg::Align::Left);
paint.set_text_baseline(femtovg::Baseline::Top);
paint.set_font(&vec![store.font_id.unwrap()]);
paint.set_font(&[store.font_id.unwrap()]);
// let should_wrap = store.text_wrap.get(&node).copied().unwrap_or_default();
let text_wrap = store.text_wrap.get(node).copied().unwrap_or_default();

Expand Down
70 changes: 70 additions & 0 deletions examples/wrap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
mod common;
use common::*;

fn main() {
let mut world = World::default();

let root = world.add(None);
world.set_width(root, Units::Pixels(600.0));
world.set_height(root, Units::Pixels(600.0));
world.set_padding(root, Units::Pixels(20.0));
world.set_layout_type(root, LayoutType::Column);
world.set_vertical_gap(root, Units::Pixels(20.0));
world.set_alignment(root, Alignment::TopLeft);

// Row wrapping example
let row_wrap_container = world.add(Some(root));
world.set_width(row_wrap_container, Units::Stretch(1.0));
world.set_height(row_wrap_container, Units::Auto);
world.set_layout_type(row_wrap_container, LayoutType::Row);
world.set_wrap(row_wrap_container, LayoutWrap::Wrap);
world.set_horizontal_gap(row_wrap_container, Units::Pixels(10.0));
world.set_vertical_gap(row_wrap_container, Units::Pixels(10.0));
world.set_alignment(row_wrap_container, Alignment::TopLeft);

// Add items to row wrap container
for _i in 0..6 {
let item = world.add(Some(row_wrap_container));
world.set_width(item, Units::Pixels(80.0));
world.set_height(item, Units::Pixels(60.0));
}

// Column wrapping example
let col_wrap_container = world.add(Some(root));
world.set_width(col_wrap_container, Units::Pixels(200.0));
world.set_height(col_wrap_container, Units::Pixels(250.0));
world.set_layout_type(col_wrap_container, LayoutType::Column);
world.set_wrap(col_wrap_container, LayoutWrap::Wrap);
world.set_horizontal_gap(col_wrap_container, Units::Pixels(10.0));
world.set_vertical_gap(col_wrap_container, Units::Pixels(10.0));
world.set_alignment(col_wrap_container, Alignment::TopLeft);

// Add items to column wrap container
for _i in 0..6 {
let item = world.add(Some(col_wrap_container));
world.set_width(item, Units::Pixels(60.0));
world.set_height(item, Units::Pixels(50.0));
}

// RTL wrapping example
let rtl_wrap_container = world.add(Some(root));
world.set_width(rtl_wrap_container, Units::Pixels(300.0));
world.set_height(rtl_wrap_container, Units::Auto);
world.set_layout_type(rtl_wrap_container, LayoutType::Row);
world.set_direction(rtl_wrap_container, Direction::RightToLeft);
world.set_wrap(rtl_wrap_container, LayoutWrap::Wrap);
world.set_horizontal_gap(rtl_wrap_container, Units::Pixels(10.0));
world.set_vertical_gap(rtl_wrap_container, Units::Pixels(10.0));
world.set_alignment(rtl_wrap_container, Alignment::TopLeft);

// Add items to RTL wrap container
for _i in 0..4 {
let item = world.add(Some(rtl_wrap_container));
world.set_width(item, Units::Pixels(100.0));
world.set_height(item, Units::Pixels(50.0));
}

root.layout(&mut world.cache, &world.tree, &world.store, &mut ());

render(world, root);
}
Loading
Loading