Skip to content

Commit

Permalink
wip hframe core
Browse files Browse the repository at this point in the history
  • Loading branch information
noxware committed Jun 11, 2024
1 parent 65c3554 commit 77a655b
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"rust-analyzer.linkedProjects": ["./examples/demo/Cargo.toml"]
"rust-analyzer.linkedProjects": [
"./examples/demo/Cargo.toml",
"./hframe-core/Cargo.toml"
]
}
7 changes: 7 additions & 0 deletions hframe-core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions hframe-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "hframe-core"
version = "0.1.0"
edition = "2021"

[dependencies]
20 changes: 20 additions & 0 deletions hframe-core/src/composed_area.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::{geo::*, id::*};

#[derive(Debug, Clone)]
pub(crate) struct ComposedHtml {
pub(crate) content: String,
}

#[derive(Debug, Clone)]
pub(crate) enum ComposedAreaKind {
Canvas,
Html(ComposedHtml),
}

#[derive(Debug, Clone)]
pub(crate) struct ComposedArea {
pub(crate) id: Id,
pub(crate) size: Size,
pub(crate) abs_pos: Pos,
pub(crate) kind: ComposedAreaKind,
}
54 changes: 54 additions & 0 deletions hframe-core/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::{
composed_area::ComposedArea, platform::Platform, test_platform::TestPlatform, tree::Node,
};

struct Context<P: Platform> {
tree: Node<ComposedArea>,
platform: P,
}

#[cfg(test)]
mod tests {
use crate::{
composed_area::{ComposedAreaKind, ComposedHtml},
geo::{Pos, Size},
id::Id,
};

use super::*;

#[test]
fn it_works() {
let ctx = Context {
tree: Node::new(ComposedArea {
id: Id::from("root"),
size: Size::new(100.0, 100.0),
abs_pos: Pos::new(0.0, 0.0),
kind: ComposedAreaKind::Canvas,
})
.nest(
Node::new(ComposedArea {
id: Id::from("child"),
size: Size::new(50.0, 50.0),
abs_pos: Pos::new(10.0, 10.0),
kind: ComposedAreaKind::Canvas,
})
.nest(Node::new(ComposedArea {
id: Id::from("grandchild"),
size: Size::new(25.0, 25.0),
abs_pos: Pos::new(5.0, 5.0),
kind: ComposedAreaKind::Html(ComposedHtml {
content: "<div>hello</div>".into(),
}),
})),
),
platform: TestPlatform {
mouse_pos: Pos::new(0.0, 0.0),
},
};

ctx.tree
.find(|node| node.read(|data| data.value.id == Id::from("grandchild")))
.unwrap();
}
}
37 changes: 37 additions & 0 deletions hframe-core/src/geo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub(crate) struct Pos {
pub(crate) x: f64,
pub(crate) y: f64,
}

impl Pos {
pub(crate) fn new(x: f64, y: f64) -> Self {
Pos { x, y }
}
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub(crate) struct Size {
pub(crate) width: f64,
pub(crate) height: f64,
}

impl Size {
pub(crate) fn new(width: f64, height: f64) -> Self {
Size { width, height }
}
}

/*
#[derive(Debug, Clone, Copy, PartialEq, Default)]
struct Rect {
pos: Pos,
size: Size,
}
impl Rect {
fn new(pos: Pos, size: Size) -> Self {
Rect { pos, size }
}
}
*/
34 changes: 34 additions & 0 deletions hframe-core/src/id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::hash::{DefaultHasher, Hash, Hasher};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Id(u64);

impl From<u64> for Id {
fn from(id: u64) -> Self {
Id(id)
}
}

impl From<&str> for Id {
fn from(id: &str) -> Self {
let mut hasher = DefaultHasher::new();
id.hash(&mut hasher);
Id(hasher.finish())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let id = Id::from("hello");
assert_eq!(id, Id::from("hello"));
assert_ne!(id, Id::from("world"));

let id = Id::from(42);
assert_eq!(id, Id::from(42));
assert_ne!(id, Id::from(43));
}
}
23 changes: 23 additions & 0 deletions hframe-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
mod composed_area;
mod context;
mod geo;
mod id;
mod platform;
mod test_platform;
mod tree;
mod web_platform;

pub fn add(left: usize, right: usize) -> usize {
left + right
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
5 changes: 5 additions & 0 deletions hframe-core/src/platform.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use crate::geo::Pos;

pub(crate) trait Platform {
fn mouse_pos(&self) -> Pos;
}
11 changes: 11 additions & 0 deletions hframe-core/src/test_platform.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use crate::{geo::Pos, platform::Platform};

pub(crate) struct TestPlatform {
pub(crate) mouse_pos: Pos,
}

impl Platform for TestPlatform {
fn mouse_pos(&self) -> Pos {
self.mouse_pos
}
}
137 changes: 137 additions & 0 deletions hframe-core/src/tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::{cell::RefCell, fmt::Debug, rc::Rc};

struct Handle<T>(Rc<RefCell<T>>);

impl<T> Clone for Handle<T> {
fn clone(&self) -> Self {
Handle(Rc::clone(&self.0))
}
}

impl<T> Handle<T> {
fn new(value: T) -> Self {
Handle(Rc::new(RefCell::new(value)))
}

fn read<R>(&self, f: impl FnOnce(&T) -> R) -> R {
match self.0.try_borrow() {
Ok(value) => f(&value),
Err(_) => panic!("The handle can't be read because it's being written somewhere else. Hint: Search where a `read_mut` closure is being used."),
}
}

fn read_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
match self.0.try_borrow_mut() {
Ok(mut value) => f(&mut value),
Err(_) => panic!("The handle can't be written because it's being read somewhere else. Hint: Search where a `read` closure is being used."),
}
}
}

pub(crate) struct NodeData<T: Debug> {
pub(crate) value: T,
pub(crate) children: Vec<Node<T>>,
}

pub(crate) struct Node<T: Debug>(Handle<NodeData<T>>);

impl<T: Debug> Clone for Node<T> {
fn clone(&self) -> Self {
Node(self.0.clone())
}
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum Walk {
Continue,
Stop,
}

impl<T: Debug> Node<T> {
pub(crate) fn new(value: T) -> Self {
Node(Handle::new(NodeData {
value,
children: Vec::new(),
}))
}

pub(crate) fn nest(&self, node: Node<T>) -> Node<T> {
self.read_mut(|data| data.children.push(node));
self.clone()
}

fn walk_impl(&self, f: &mut impl FnMut(Node<T>, usize) -> Walk, depth: usize) -> Walk {
if f(self.clone(), depth) == Walk::Stop {
return Walk::Stop;
}

self.read(|data| {
for child in data.children.iter() {
if child.walk_impl(f, depth + 1) == Walk::Stop {
return Walk::Stop;
}
}

Walk::Continue
})
}

pub(crate) fn walk(&self, mut f: impl FnMut(Node<T>, usize) -> Walk) {
self.walk_impl(&mut f, 0);
}

// kepp this private to fully hide the handle
fn data(&self) -> Handle<NodeData<T>> {
self.0.clone()
}

pub(crate) fn find(&self, predicate: impl Fn(Node<T>) -> bool) -> Option<Node<T>> {
let mut found = None;

self.walk(|node, _| {
if predicate(node.clone()) {
found = Some(node.clone());
Walk::Stop
} else {
Walk::Continue
}
});

found
}

pub(crate) fn read<R>(&self, f: impl FnOnce(&NodeData<T>) -> R) -> R {
self.data().read(|data| f(&data))
}

pub(crate) fn read_mut<R>(&self, f: impl FnOnce(&mut NodeData<T>) -> R) -> R {
self.data().read_mut(|data| f(data))
}
}

impl Debug for Node<String> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
self.walk(|node, depth| {
node.read(|data| {
writeln!(f, "{:indent$}{:?}", "", data.value, indent = depth * 2).unwrap();
Walk::Continue
})
});
} else {
self.read(|data| {
write!(f, "{:?}", data.value).unwrap();

if !data.children.is_empty() {
write!(f, " {{").unwrap();
for child in data.children.iter() {
write!(f, " {:?}", child).unwrap();
}
write!(f, " }}").unwrap();
}
});
}

Ok(())
}
}
9 changes: 9 additions & 0 deletions hframe-core/src/web_platform.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::platform::Platform;

pub struct WebPlatform;

impl Platform for WebPlatform {
fn mouse_pos(&self) -> crate::geo::Pos {
crate::geo::Pos::new(0.0, 0.0)
}
}

0 comments on commit 77a655b

Please sign in to comment.