Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 19 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"crates/craft_resource_manager",
"crates/craft_runtime",
"crates/craft_undo",
"website",
]

[workspace.package]
Expand Down
6 changes: 6 additions & 0 deletions crates/craft_retained/src/elements/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ impl Default for Container {

impl Element for Container {}

impl Drop for ContainerInner {
fn drop(&mut self) {
ElementInternals::drop(self)
}
}

impl AsElement for Container {
fn as_element_rc(&self) -> Rc<RefCell<dyn ElementInternals>> {
self.inner.clone()
Expand Down
23 changes: 20 additions & 3 deletions crates/craft_retained/src/elements/dropdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ impl Default for Dropdown {

impl Element for Dropdown {}

impl Drop for DropdownInner {
fn drop(&mut self) {
ElementInternals::drop(self)
}
}

impl AsElement for Dropdown {
fn as_element_rc(&self) -> Rc<RefCell<dyn ElementInternals>> {
self.inner.clone()
Expand Down Expand Up @@ -582,7 +588,7 @@ impl DropdownInner {
// Remove the old selected element from the layout tree.
if let Some(old_selected_element) = &self.selected_element {
TAFFY_TREE.with_borrow_mut(|taffy_tree| {
taffy_tree.remove_subtree(old_selected_element.borrow().element_data().layout.taffy_node_id());
taffy_tree.unparent_node(old_selected_element.borrow().element_data().layout.taffy_node_id());
});
}

Expand All @@ -609,7 +615,12 @@ impl DropdownInner {
});
}

fn update_most_recently_hovered_child(&mut self, message: &EventKind, list_box: Rectangle, list_scroll_box: Rectangle) {
fn update_most_recently_hovered_child(
&mut self,
message: &EventKind,
list_box: Rectangle,
list_scroll_box: Rectangle,
) {
if let EventKind::PointerMovedEvent(pb) = message {
let pointer_position = Point::new(pb.current.position.x, pb.current.position.y);
let is_pointer_in_list = list_box.contains(&pointer_position);
Expand Down Expand Up @@ -656,7 +667,13 @@ impl DropdownInner {
}
}

fn handle_child_click(&mut self, event: &mut Event, pointer_position: &Point, is_pointer_in_window: bool, is_pointer_in_scrollbar: bool) {
fn handle_child_click(
&mut self,
event: &mut Event,
pointer_position: &Point,
is_pointer_in_window: bool,
is_pointer_in_scrollbar: bool,
) {
if is_pointer_in_window && !is_pointer_in_scrollbar {
let mut should_hide_window = false;
for (child_index, child) in self.children().iter().cloned().enumerate() {
Expand Down
6 changes: 6 additions & 0 deletions crates/craft_retained/src/elements/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ impl crate::elements::ElementData for ImageInner {

impl Element for Image {}

impl Drop for ImageInner {
fn drop(&mut self) {
ElementInternals::drop(self)
}
}

impl AsElement for Image {
fn as_element_rc(&self) -> Rc<RefCell<dyn ElementInternals>> {
self.inner.clone()
Expand Down
6 changes: 6 additions & 0 deletions crates/craft_retained/src/elements/slider/slider_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ impl SliderInner {

impl Element for Slider {}

impl Drop for SliderInner {
fn drop(&mut self) {
ElementInternals::drop(self)
}
}

impl AsElement for Slider {
fn as_element_rc(&self) -> Rc<RefCell<dyn ElementInternals>> {
self.inner.clone()
Expand Down
6 changes: 6 additions & 0 deletions crates/craft_retained/src/elements/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ pub struct TextState {

impl Element for Text {}

impl Drop for TextInner {
fn drop(&mut self) {
ElementInternals::drop(self)
}
}

impl AsElement for Text {
fn as_element_rc(&self) -> Rc<RefCell<dyn ElementInternals>> {
self.inner.clone()
Expand Down
6 changes: 6 additions & 0 deletions crates/craft_retained/src/elements/text_input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ impl TextInput {

impl Element for TextInput {}

impl Drop for TextInputInner {
fn drop(&mut self) {
ElementInternals::drop(self)
}
}

impl AsElement for TextInput {
fn as_element_rc(&self) -> Rc<RefCell<dyn ElementInternals>> {
self.inner.clone()
Expand Down
50 changes: 36 additions & 14 deletions crates/craft_retained/src/elements/traits/element_internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use craft_renderer::RenderList;

use crate::app::{ELEMENTS, FOCUS, TAFFY_TREE};
use crate::elements::scrollable::{ScrollState, draw_scrollbar};
use crate::elements::{ElementData, ElementIdMap, ScrollOptions, WindowInternal};
use crate::elements::{ElementData, ScrollOptions, WindowInternal};
use crate::events::pointer_capture::PointerCapture;
use crate::events::{DropdownItemSelectedHandler, Event, EventKind, KeyboardInputHandler, PointerCaptureHandler, PointerEnterHandler, PointerEventHandler, PointerLeaveHandler, PointerUpdateHandler, ScrollHandler, SliderValueChangedHandler};
use crate::layout::TaffyTree;
Expand All @@ -23,7 +23,10 @@ use crate::text::text_context::TextContext;
use crate::{Color, CraftError};

/// Internal element methods that should typically be ignored by users. Public for custom elements.
pub trait ElementInternals: ElementData + Any {
///
/// Drop is required to clean up any taffy nodes allocated by the element.
#[allow(drop_bounds)]
pub trait ElementInternals: ElementData + Any + Drop {
fn deep_clone(&self) -> Rc<RefCell<dyn ElementInternals>>;

fn position_in_parent(&self) -> Option<usize> {
Expand Down Expand Up @@ -444,36 +447,29 @@ pub trait ElementInternals: ElementData + Any {

// Remove the parent reference.
child.borrow_mut().element_data_mut().parent = None;
child.borrow_mut().element_data_mut().window = None;
//child.borrow_mut().element_data_mut().window = None;
child.borrow_mut().propagate_window_down();

TAFFY_TREE.with_borrow_mut(|taffy_tree| {
let child_id = child.borrow().element_data().layout.taffy_node_id;

if let Some(child_id) = child_id {
taffy_tree.remove_subtree(child_id);
taffy_tree.unparent_node(child_id);
}

let parent_id = self.element_data().layout.taffy_node_id;
taffy_tree.mark_dirty(parent_id.unwrap());
});

// TODO: Move to document
fn remove_element_from_document(
node: Rc<RefCell<dyn ElementInternals>>,
pointer_capture: &mut PointerCapture,
elements: &mut ElementIdMap,
) {
elements.remove_id(node.borrow().element_data().internal_id);
fn remove_element_from_document(node: Rc<RefCell<dyn ElementInternals>>, pointer_capture: &mut PointerCapture) {
pointer_capture.remove_element(&node);
for child in node.borrow().children() {
remove_element_from_document(child.clone(), pointer_capture, elements);
remove_element_from_document(child.clone(), pointer_capture);
}
}

ELEMENTS.with_borrow_mut(|elements| {
remove_element_from_document(child.clone(), &mut self.pointer_capture().borrow_mut(), elements);
});
remove_element_from_document(child.clone(), &mut self.pointer_capture().borrow_mut());

child.borrow_mut().unfocus();

Expand Down Expand Up @@ -938,6 +934,32 @@ pub trait ElementInternals: ElementData + Any {
.winit_window
.clone()
}

/// Recursively prints the IDs of this element and all of its descendants.
fn print_tree_ids(&self, depth: usize) {
let indent = " ".repeat(depth);

// Access the ID from element_data.
// If it's None, we can print "Unnamed Element" or the internal_id.
let id_label = self.element_data().internal_id.to_string();

println!("{}└─ {}: {}", indent, id_label, self.element_data().window.is_some());

for child in self.children() {
child.borrow().print_tree_ids(depth + 1);
}
}

fn drop(&mut self) {
if let Some(taffy_node) = self.element_data().layout.taffy_node_id {
TAFFY_TREE.with_borrow_mut(|taffy_tree| {
taffy_tree.remove_node(taffy_node);
});
}
ELEMENTS.with_borrow_mut(|elements| {
elements.remove_id(self.element_data().internal_id);
});
}
}

pub fn resolve_clip_for_scrollable(element: &mut dyn ElementInternals, clip_bounds: Option<Rectangle>) {
Expand Down
6 changes: 6 additions & 0 deletions crates/craft_retained/src/elements/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ impl Default for Window {

impl Element for Window {}

impl Drop for WindowInternal {
fn drop(&mut self) {
ElementInternals::drop(self)
}
}

impl AsElement for Window {
fn as_element_rc(&self) -> Rc<RefCell<dyn ElementInternals>> {
self.inner.clone()
Expand Down
21 changes: 18 additions & 3 deletions crates/craft_retained/src/layout/taffy_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,31 @@ impl TaffyTree {
self.is_layout_dirty = false;
}

/// Remove the entire layout subtree.
/// Remove a specific `node` and its ancestors from the tree and drop it
pub fn remove_subtree(&mut self, node: NodeId) {
// Can we avoid this allocation?
let children = self.inner.children(node).unwrap();

for child in children {
self.remove_subtree(child);
}
self.remove_node(node);
self.request_layout();
}

/// Removes the `node`.
///
/// The `node` is not removed from the tree entirely, it is simply no longer attached to its previous parent.
pub fn unparent_node(&mut self, node: NodeId) {
if let Some(parent) = self.inner.parent(node) {
self.inner.remove_child(parent, node).unwrap();
self.request_layout();
}
}

self.inner.remove(node).map(|_| ()).unwrap();
/// Remove a specific node from the tree and drop it
pub fn remove_node(&mut self, node: NodeId) {
self.inner.remove(node).unwrap();
self.request_layout();
}

Expand Down Expand Up @@ -158,7 +173,7 @@ impl TaffyTree {
#[inline(always)]
pub fn request_layout(&mut self) {
self.is_layout_dirty = true;
self.is_apply_layout_dirty = Vec::new();
self.is_apply_layout_dirty.clear();
}

#[inline(always)]
Expand Down
2 changes: 1 addition & 1 deletion crates/craft_retained/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub use image;

#[cfg(target_os = "android")]
pub use winit::platform::android::activity::*;
pub use winit::window::{Cursor, CursorIcon, WindowAttributes};
pub use winit::window::{Cursor, CursorIcon, Window as WinitWindow, WindowAttributes};

pub use crate::craftcallback::CraftCallback;
pub use crate::options::CraftOptions;
Expand Down
54 changes: 54 additions & 0 deletions website/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[package]
name = "website"
version = "0.1.0"
edition = "2024"

[dependencies]

tracing-subscriber = "0.3.19"
tracing = "0.1.41"

util = { path = "../examples/util" }

serde = { version = "1.0.213", features = ["derive"] }
serde_json = "1.0.133"
web-sys = { version = "0.3.77", features = ["Window", "Location", "History"] }

[target.'cfg(target_arch = "wasm32")'.dependencies.craft_retained]
path = "../crates/craft_retained"
default-features = false
features = [
"vello_hybrid_renderer",
"vello_hybrid_renderer",
"http_client",
"png",
"jpeg",
"accesskit",
"system_fonts",
"markdown"
]

[target.'cfg(not(target_arch = "wasm32"))'.dependencies.craft_retained]
path = "../crates/craft_retained"
default-features = false
features = [
"vello_renderer",
"http_client",
"png",
"jpeg",
"accesskit",
"system_fonts",
"markdown"
]

[target.'cfg(target_arch = "wasm32")'.dependencies]
tracing-web = "0.1.3"
console_error_panic_hook = "0.1.7"

[dependencies.reqwest]
workspace = true
default-features = false
features = ["rustls", "json"]

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
open = "5"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Loading