diff --git a/README.md b/README.md index 8c20fcc0..7457644e 100644 --- a/README.md +++ b/README.md @@ -13,25 +13,21 @@ fn app_view() -> impl View { let (counter, set_counter) = create_signal(0); // create user interface with Floem view functions - stack(|| { - ( - label(move || format!("Value: {}", counter.get())), - stack(|| { - ( - label(|| "Increment") - .on_click(move |_| { - set_counter.update(|value| *value += 1); - true - }), - label(|| "Decrement") - .on_click(move |_| { - set_counter.update(|value| *value -= 1); - true - }), - ) - }), - ) - }) + stack(( + label(move || format!("Value: {}", counter.get())), + stack(( + text("Increment") + .on_click(move |_| { + set_counter.update(|value| *value += 1); + true + }), + text("Decrement") + .on_click(move |_| { + set_counter.update(|value| *value -= 1); + true + }), + )), + )) } fn main() { diff --git a/examples/animations/src/main.rs b/examples/animations/src/main.rs index 9a95b09d..0edd83d6 100644 --- a/examples/animations/src/main.rs +++ b/examples/animations/src/main.rs @@ -13,7 +13,7 @@ fn app_view() -> impl View { let (counter, set_counter) = create_signal(0.0); let (is_hovered, set_is_hovered) = create_signal(false); - stack(|| { + stack({ (label(|| "Hover or click me!") .on_click(move |_| { set_counter.update(|value| *value += 1.0); diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index ccf5ba31..fa9d9c02 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -2,74 +2,70 @@ use floem::{ peniko::Color, reactive::create_signal, view::View, - views::{label, stack, Decorators}, + views::{label, stack, text, Decorators}, }; fn app_view() -> impl View { let (counter, set_counter) = create_signal(0); - stack(|| { - ( - label(move || format!("Value: {}", counter.get())).style(|s| s.padding_px(10.0)), - stack(|| { - ( - label(|| "Increment") - .style(|s| { - s.border_radius(10.0) - .padding_px(10.0) - .background(Color::WHITE) - .box_shadow_blur(5.0) - }) - .on_click({ - move |_| { - set_counter.update(|value| *value += 1); - true - } - }) - .hover_style(|s| s.background(Color::LIGHT_GREEN)) - .active_style(|s| s.color(Color::WHITE).background(Color::DARK_GREEN)) - .keyboard_navigatable() - .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), - label(|| "Decrement") - .on_click({ - move |_| { - set_counter.update(|value| *value -= 1); - true - } - }) - .style(|s| { - s.box_shadow_blur(5.0) - .background(Color::WHITE) - .border_radius(10.0) - .padding_px(10.0) - .margin_left_px(10.0) - }) - .hover_style(|s| s.background(Color::rgb8(244, 67, 54))) - .active_style(|s| s.color(Color::WHITE).background(Color::RED)) - .keyboard_navigatable() - .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), - label(|| "Reset to 0") - .on_click(move |_| { - println!("Reset counter pressed"); // will not fire if button is disabled - set_counter.update(|value| *value = 0); - true - }) - .disabled(move || counter.get() == 0) - .style(|s| { - s.box_shadow_blur(5.0) - .border_radius(10.0) - .padding_px(10.0) - .margin_left_px(10.0) - .background(Color::LIGHT_BLUE) - }) - .disabled_style(|s| s.background(Color::LIGHT_GRAY)) - .hover_style(|s| s.background(Color::LIGHT_YELLOW)) - .active_style(|s| s.color(Color::WHITE).background(Color::YELLOW_GREEN)) - .keyboard_navigatable() - .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), - ) - }), - ) - }) + stack(( + label(move || format!("Value: {}", counter.get())).style(|s| s.padding_px(10.0)), + stack(( + text("Increment") + .style(|s| { + s.border_radius(10.0) + .padding_px(10.0) + .background(Color::WHITE) + .box_shadow_blur(5.0) + }) + .on_click({ + move |_| { + set_counter.update(|value| *value += 1); + true + } + }) + .hover_style(|s| s.background(Color::LIGHT_GREEN)) + .active_style(|s| s.color(Color::WHITE).background(Color::DARK_GREEN)) + .keyboard_navigatable() + .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), + text("Decrement") + .on_click({ + move |_| { + set_counter.update(|value| *value -= 1); + true + } + }) + .style(|s| { + s.box_shadow_blur(5.0) + .background(Color::WHITE) + .border_radius(10.0) + .padding_px(10.0) + .margin_left_px(10.0) + }) + .hover_style(|s| s.background(Color::rgb8(244, 67, 54))) + .active_style(|s| s.color(Color::WHITE).background(Color::RED)) + .keyboard_navigatable() + .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), + text("Reset to 0") + .on_click(move |_| { + println!("Reset counter pressed"); // will not fire if button is disabled + set_counter.update(|value| *value = 0); + true + }) + .disabled(move || counter.get() == 0) + .style(|s| { + s.box_shadow_blur(5.0) + .border_radius(10.0) + .padding_px(10.0) + .margin_left_px(10.0) + .background(Color::LIGHT_BLUE) + }) + .disabled_style(|s| s.background(Color::LIGHT_GRAY)) + .hover_style(|s| s.background(Color::LIGHT_YELLOW)) + .active_style(|s| s.color(Color::WHITE).background(Color::YELLOW_GREEN)) + .keyboard_navigatable() + .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), + )), + )) .style(|s| { s.size_pct(100.0, 100.0) .flex_col() diff --git a/examples/responsive/src/main.rs b/examples/responsive/src/main.rs index b5882fdb..e0fd768d 100644 --- a/examples/responsive/src/main.rs +++ b/examples/responsive/src/main.rs @@ -6,7 +6,7 @@ use floem::{ }; fn app_view() -> impl View { - stack(|| { + stack({ (label(|| "Resize the window to see the magic") .style(|s| { s.border(1.0) diff --git a/examples/virtual_list/src/main.rs b/examples/virtual_list/src/main.rs index 2544d02d..f49a2ded 100644 --- a/examples/virtual_list/src/main.rs +++ b/examples/virtual_list/src/main.rs @@ -10,8 +10,8 @@ fn app_view() -> impl View { let long_list: im::Vector = (0..1000000).collect(); let (long_list, _set_long_list) = create_signal(long_list); - container(move || { - scroll(move || { + container( + scroll( virtual_list( VirtualListDirection::Vertical, VirtualListItemSize::Fixed(Box::new(|| 20.0)), @@ -19,10 +19,10 @@ fn app_view() -> impl View { move |item| *item, move |item| label(move || item.to_string()).style(|s| s.height_px(20.0)), ) - .style(|s| s.flex_col()) - }) - .style(|s| s.width_px(100.0).height_pct(100.0).border(1.0)) - }) + .style(|s| s.flex_col()), + ) + .style(|s| s.width_px(100.0).height_pct(100.0).border(1.0)), + ) .style(|s| { s.size_pct(100.0, 100.0) .padding_vert_px(20.0) diff --git a/examples/widget-gallery/src/buttons.rs b/examples/widget-gallery/src/buttons.rs index e7f81659..9aaa298f 100644 --- a/examples/widget-gallery/src/buttons.rs +++ b/examples/widget-gallery/src/buttons.rs @@ -8,7 +8,7 @@ use floem::{ use crate::form::{form, form_item}; pub fn button_view() -> impl View { - form(|| { + form({ ( form_item("Basic Button:".to_string(), 120.0, || { label(|| "Click me") diff --git a/examples/widget-gallery/src/checkbox.rs b/examples/widget-gallery/src/checkbox.rs index 286c328a..131e8e35 100644 --- a/examples/widget-gallery/src/checkbox.rs +++ b/examples/widget-gallery/src/checkbox.rs @@ -9,7 +9,7 @@ use crate::form::{form, form_item}; pub fn checkbox_view() -> impl View { let (is_checked, set_is_checked) = create_signal(true); - form(move || { + form({ ( form_item("Basic Checkbox:".to_string(), 120.0, move || { checkbox(is_checked) @@ -20,7 +20,7 @@ pub fn checkbox_view() -> impl View { }) }), form_item("Labelled Checkbox:".to_string(), 120.0, move || { - stack(|| { + stack({ ( checkbox(is_checked) .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), @@ -33,7 +33,7 @@ pub fn checkbox_view() -> impl View { }) }), form_item("Disabled Checkbox:".to_string(), 120.0, move || { - stack(|| { + stack({ ( checkbox(is_checked) .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), diff --git a/examples/widget-gallery/src/context_menu.rs b/examples/widget-gallery/src/context_menu.rs index bba08c44..5d0b7f70 100644 --- a/examples/widget-gallery/src/context_menu.rs +++ b/examples/widget-gallery/src/context_menu.rs @@ -5,7 +5,7 @@ use floem::{ }; pub fn menu_view() -> impl View { - stack(move || { + stack({ ( label(|| "Click me (Popout menu)") .base_style(|s| s.padding_px(10.0).margin_bottom_px(10.0).border(1.0)) diff --git a/examples/widget-gallery/src/form.rs b/examples/widget-gallery/src/form.rs index d93be1ff..96fd96ae 100644 --- a/examples/widget-gallery/src/form.rs +++ b/examples/widget-gallery/src/form.rs @@ -5,7 +5,7 @@ use floem::{ views::{container, label, stack, Decorators}, }; -pub fn form(children: impl FnOnce() -> VT) -> impl View { +pub fn form(children: VT) -> impl View { stack(children).style(|s| { s.flex_col() .items_start() @@ -20,18 +20,14 @@ pub fn form_item( label_width: f32, view_fn: impl Fn() -> V, ) -> impl View { - container(|| { - stack(|| { - ( - container(|| { - label(move || item_label.clone()).style(|s| s.font_weight(Weight::BOLD)) - }) + container( + stack(( + container(label(move || item_label.clone()).style(|s| s.font_weight(Weight::BOLD))) .style(move |s| s.width_px(label_width).justify_end().margin_right_px(10.0)), - view_fn(), - ) - }) - .style(|s| s.flex_row().items_start()) - }) + view_fn(), + )) + .style(|s| s.flex_row().items_start()), + ) .style(|s| { s.flex_row() .items_center() diff --git a/examples/widget-gallery/src/inputs.rs b/examples/widget-gallery/src/inputs.rs index db64b204..cfb4a4a8 100644 --- a/examples/widget-gallery/src/inputs.rs +++ b/examples/widget-gallery/src/inputs.rs @@ -11,7 +11,7 @@ use crate::form::{form, form_item}; pub fn text_input_view() -> impl View { let text = create_rw_signal("".to_string()); - form(move || { + form({ ( form_item("Simple Input:".to_string(), 120.0, move || { text_input(text) diff --git a/examples/widget-gallery/src/labels.rs b/examples/widget-gallery/src/labels.rs index 41842073..ebb76062 100644 --- a/examples/widget-gallery/src/labels.rs +++ b/examples/widget-gallery/src/labels.rs @@ -8,7 +8,7 @@ use floem::{ use crate::form::{form, form_item}; pub fn label_view() -> impl View { - form(|| { + form({ ( form_item("Simple Label:".to_string(), 120.0, || { label(move || "This is a simple label".to_owned()) diff --git a/examples/widget-gallery/src/lists.rs b/examples/widget-gallery/src/lists.rs index a8860c2e..689172e3 100644 --- a/examples/widget-gallery/src/lists.rs +++ b/examples/widget-gallery/src/lists.rs @@ -15,10 +15,10 @@ use floem::{ use crate::form::{form, form_item}; pub fn virt_list_view() -> impl View { - stack(|| { + stack({ ( - form(|| (form_item("Simple List".to_string(), 120.0, simple_list),)), - form(|| (form_item("Enhanced List".to_string(), 120.0, enhanced_list),)), + form((form_item("Simple List".to_string(), 120.0, simple_list),)), + form((form_item("Enhanced List".to_string(), 120.0, enhanced_list),)), ) }) } @@ -26,7 +26,7 @@ pub fn virt_list_view() -> impl View { fn simple_list() -> impl View { let long_list: im::Vector = (0..100).collect(); let (long_list, _set_long_list) = create_signal(long_list); - scroll(move || { + scroll( virtual_list( VirtualListDirection::Vertical, VirtualListItemSize::Fixed(Box::new(|| 20.0)), @@ -34,8 +34,8 @@ fn simple_list() -> impl View { move |item| *item, move |item| label(move || item.to_string()).style(|s| s.height_px(24.0)), ) - .style(|s| s.flex_col()) - }) + .style(|s| s.flex_col()), + ) .style(|s| s.width_px(100.0).height_px(300.0).border(1.0)) } @@ -46,7 +46,7 @@ fn enhanced_list() -> impl View { let (selected, set_selected) = create_signal(0); let list_width = 180.0; let item_height = 32.0; - scroll(move || { + scroll( virtual_list( VirtualListDirection::Vertical, VirtualListItemSize::Fixed(Box::new(|| 32.0)), @@ -59,8 +59,8 @@ fn enhanced_list() -> impl View { .position(|it| *it == item) .unwrap(); let (is_checked, set_is_checked) = create_signal(true); - container(move || { - stack(move || { + container({ + stack({ ( checkbox(is_checked).on_click(move |_| { set_is_checked.update(|checked: &mut bool| *checked = !*checked); @@ -68,7 +68,7 @@ fn enhanced_list() -> impl View { }), label(move || item.to_string()) .style(|s| s.height_px(32.0).font_size(32.0)), - container(move || { + container({ label(move || " X ") .on_click(move |_| { print!("Item Removed"); @@ -139,7 +139,7 @@ fn enhanced_list() -> impl View { .hover_style(|s| s.background(Color::LIGHT_GRAY).cursor(CursorStyle::Pointer)) }, ) - .style(move |s| s.flex_col().width_px(list_width)) - }) + .style(move |s| s.flex_col().width_px(list_width)), + ) .style(move |s| s.width_px(list_width).height_px(300.0).border(1.0)) } diff --git a/examples/widget-gallery/src/main.rs b/examples/widget-gallery/src/main.rs index 5b72de14..80a6030c 100644 --- a/examples/widget-gallery/src/main.rs +++ b/examples/widget-gallery/src/main.rs @@ -29,10 +29,10 @@ fn app_view() -> impl View { let (tabs, _set_tabs) = create_signal(tabs); let (active_tab, set_active_tab) = create_signal(0); - stack(|| { + stack({ ( - container(move || { - scroll(move || { + container({ + scroll({ virtual_list( VirtualListDirection::Vertical, VirtualListItemSize::Fixed(Box::new(|| 32.0)), @@ -44,7 +44,7 @@ fn app_view() -> impl View { .iter() .position(|it| *it == item) .unwrap(); - stack(|| (label(move || item).style(|s| s.font_size(24.0)),)) + stack((label(move || item).style(|s| s.font_size(24.0)),)) .on_click(move |_| { set_active_tab.update(|v: &mut usize| { *v = tabs @@ -113,20 +113,20 @@ fn app_view() -> impl View { .flex_col() .items_center() }), - container(move || { + container({ tab( move || active_tab.get(), move || tabs.get(), |it| *it, |it| match it { - "Label" => container_box(|| Box::new(labels::label_view())), - "Button" => container_box(|| Box::new(buttons::button_view())), - "Checkbox" => container_box(|| Box::new(checkbox::checkbox_view())), - "Input" => container_box(|| Box::new(inputs::text_input_view())), - "List" => container_box(|| Box::new(lists::virt_list_view())), - "Menu" => container_box(|| Box::new(context_menu::menu_view())), - "RichText" => container_box(|| Box::new(rich_text::rich_text_view())), - _ => container_box(|| Box::new(label(|| "Not implemented".to_owned()))), + "Label" => container_box(labels::label_view()), + "Button" => container_box(buttons::button_view()), + "Checkbox" => container_box(checkbox::checkbox_view()), + "Input" => container_box(inputs::text_input_view()), + "List" => container_box(lists::virt_list_view()), + "Menu" => container_box(context_menu::menu_view()), + "RichText" => container_box(rich_text::rich_text_view()), + _ => container_box(label(|| "Not implemented".to_owned())), }, ) .style(|s| s.size_pct(100.0, 100.0)) diff --git a/examples/widget-gallery/src/rich_text.rs b/examples/widget-gallery/src/rich_text.rs index 791b8f62..4bf71a44 100644 --- a/examples/widget-gallery/src/rich_text.rs +++ b/examples/widget-gallery/src/rich_text.rs @@ -14,7 +14,7 @@ pub fn rich_text_view() -> impl View { println(\"Hello World!\"); }"; // ddddd - scroll(move || { + scroll({ rich_text(move || { let attrs = Attrs::new().color(Color::BLACK); diff --git a/examples/window-scale/src/main.rs b/examples/window-scale/src/main.rs index ecee6d14..ee437ce8 100644 --- a/examples/window-scale/src/main.rs +++ b/examples/window-scale/src/main.rs @@ -8,110 +8,108 @@ use floem::{ fn app_view() -> impl View { let (counter, set_counter) = create_signal(0); let window_scale = create_rw_signal(1.0); - stack(|| { - ( - label(move || format!("Value: {}", counter.get())).style(|s| s.padding_px(10.0)), - stack(|| { - ( - label(|| "Increment") - .style(|s| s.border(1.0).border_radius(10.0).padding_px(10.0)) - .on_click(move |_| { - set_counter.update(|value| *value += 1); - true - }) - .hover_style(|s| s.background(Color::LIGHT_GREEN)) - .active_style(|s| s.color(Color::WHITE).background(Color::DARK_GREEN)) - .keyboard_navigatable() - .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), - label(|| "Decrement") - .on_click(move |_| { - set_counter.update(|value| *value -= 1); - true - }) - .style(|s| { - s.border(1.0) - .border_radius(10.0) - .padding_px(10.0) - .margin_left_px(10.0) - }) - .hover_style(|s| s.background(Color::rgb8(244, 67, 54))) - .active_style(|s| s.color(Color::WHITE).background(Color::RED)) - .keyboard_navigatable() - .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), - label(|| "Reset to 0") - .on_click(move |_| { - println!("Reset counter pressed"); // will not fire if button is disabled - set_counter.update(|value| *value = 0); - true - }) - .disabled(move || counter.get() == 0) - .style(|s| { - s.border(1.0) - .border_radius(10.0) - .padding_px(10.0) - .margin_left_px(10.0) - .background(Color::LIGHT_BLUE) - }) - .disabled_style(|s| s.background(Color::LIGHT_GRAY)) - .hover_style(|s| s.background(Color::LIGHT_YELLOW)) - .active_style(|s| s.color(Color::WHITE).background(Color::YELLOW_GREEN)) - .keyboard_navigatable() - .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), - ) - }), - stack(|| { - ( - label(|| "Zoom In") - .on_click(move |_| { - window_scale.update(|scale| *scale *= 1.2); - true - }) - .style(|s| { - s.border(1.0) - .border_radius(10.0) - .margin_top_px(10.0) - .margin_right_px(10.0) - .padding_px(10.0) - }) - .hover_style(|s| s.background(Color::LIGHT_GREEN)), - label(|| "Zoom Out") - .on_click(move |_| { - window_scale.update(|scale| *scale /= 1.2); - true - }) - .style(|s| { - s.border(1.0) - .border_radius(10.0) - .margin_top_px(10.0) - .margin_right_px(10.0) - .padding_px(10.0) - }) - .hover_style(|s| s.background(Color::LIGHT_GREEN)), - label(|| "Zoom Reset") - .disabled(move || window_scale.get() == 1.0) - .on_click(move |_| { - window_scale.set(1.0); - true - }) - .style(|s| { - s.border(1.0) - .border_radius(10.0) - .margin_top_px(10.0) - .margin_right_px(10.0) - .padding_px(10.0) - }) - .hover_style(|s| s.background(Color::LIGHT_GREEN)) - .disabled_style(|s| s.background(Color::LIGHT_GRAY)), - ) - }) - .style(|s| { - s.absolute() - .size_pct(100.0, 100.0) - .items_start() - .justify_end() - }), - ) - }) + stack(( + label(move || format!("Value: {}", counter.get())).style(|s| s.padding_px(10.0)), + stack({ + ( + label(|| "Increment") + .style(|s| s.border(1.0).border_radius(10.0).padding_px(10.0)) + .on_click(move |_| { + set_counter.update(|value| *value += 1); + true + }) + .hover_style(|s| s.background(Color::LIGHT_GREEN)) + .active_style(|s| s.color(Color::WHITE).background(Color::DARK_GREEN)) + .keyboard_navigatable() + .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), + label(|| "Decrement") + .on_click(move |_| { + set_counter.update(|value| *value -= 1); + true + }) + .style(|s| { + s.border(1.0) + .border_radius(10.0) + .padding_px(10.0) + .margin_left_px(10.0) + }) + .hover_style(|s| s.background(Color::rgb8(244, 67, 54))) + .active_style(|s| s.color(Color::WHITE).background(Color::RED)) + .keyboard_navigatable() + .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), + label(|| "Reset to 0") + .on_click(move |_| { + println!("Reset counter pressed"); // will not fire if button is disabled + set_counter.update(|value| *value = 0); + true + }) + .disabled(move || counter.get() == 0) + .style(|s| { + s.border(1.0) + .border_radius(10.0) + .padding_px(10.0) + .margin_left_px(10.0) + .background(Color::LIGHT_BLUE) + }) + .disabled_style(|s| s.background(Color::LIGHT_GRAY)) + .hover_style(|s| s.background(Color::LIGHT_YELLOW)) + .active_style(|s| s.color(Color::WHITE).background(Color::YELLOW_GREEN)) + .keyboard_navigatable() + .focus_visible_style(|s| s.border_color(Color::BLUE).border(2.)), + ) + }), + stack({ + ( + label(|| "Zoom In") + .on_click(move |_| { + window_scale.update(|scale| *scale *= 1.2); + true + }) + .style(|s| { + s.border(1.0) + .border_radius(10.0) + .margin_top_px(10.0) + .margin_right_px(10.0) + .padding_px(10.0) + }) + .hover_style(|s| s.background(Color::LIGHT_GREEN)), + label(|| "Zoom Out") + .on_click(move |_| { + window_scale.update(|scale| *scale /= 1.2); + true + }) + .style(|s| { + s.border(1.0) + .border_radius(10.0) + .margin_top_px(10.0) + .margin_right_px(10.0) + .padding_px(10.0) + }) + .hover_style(|s| s.background(Color::LIGHT_GREEN)), + label(|| "Zoom Reset") + .disabled(move || window_scale.get() == 1.0) + .on_click(move |_| { + window_scale.set(1.0); + true + }) + .style(|s| { + s.border(1.0) + .border_radius(10.0) + .margin_top_px(10.0) + .margin_right_px(10.0) + .padding_px(10.0) + }) + .hover_style(|s| s.background(Color::LIGHT_GREEN)) + .disabled_style(|s| s.background(Color::LIGHT_GRAY)), + ) + }) + .style(|s| { + s.absolute() + .size_pct(100.0, 100.0) + .items_start() + .justify_end() + }), + )) .window_scale(move || window_scale.get()) .style(|s| { s.size_pct(100.0, 100.0) diff --git a/src/action.rs b/src/action.rs index 110be2dc..a4268321 100644 --- a/src/action.rs +++ b/src/action.rs @@ -12,16 +12,14 @@ use crate::{ ext_event::create_ext_action, file::{FileDialogOptions, FileInfo}, menu::Menu, - update::{UpdateMessage, UPDATE_MESSAGES}, - window_handle::get_current_view, + update::{UpdateMessage, CENTRAL_UPDATE_MESSAGES}, + window_handle::{get_current_view, set_current_view}, }; fn add_update_message(msg: UpdateMessage) { let current_view = get_current_view(); - UPDATE_MESSAGES.with(|msgs| { - let mut msgs = msgs.borrow_mut(); - let msgs = msgs.entry(current_view).or_default(); - msgs.push(msg); + CENTRAL_UPDATE_MESSAGES.with(|msgs| { + msgs.borrow_mut().push((current_view, msg)); }); } @@ -29,8 +27,8 @@ pub fn toggle_window_maximized() { add_update_message(UpdateMessage::ToggleWindowMaximized); } -pub fn set_handle_titlebar(val: bool) { - add_update_message(UpdateMessage::HandleTitleBar(val)); +pub fn drag_window() { + add_update_message(UpdateMessage::DragWindow); } pub fn set_window_delta(delta: Vec2) { @@ -72,6 +70,14 @@ impl TimerToken { } pub fn exec_after(duration: Duration, action: impl FnOnce(TimerToken) + 'static) -> TimerToken { + let view = get_current_view(); + let action = move |token| { + let current_view = get_current_view(); + set_current_view(view); + action(token); + set_current_view(current_view); + }; + let token = TimerToken::next(); let deadline = Instant::now() + duration; add_app_update_event(AppUpdateEvent::RequestTimer { diff --git a/src/context.rs b/src/context.rs index 6b88c803..8c1ad010 100644 --- a/src/context.rs +++ b/src/context.rs @@ -26,103 +26,6 @@ use crate::{ style::{ComputedStyle, CursorStyle, Style, StyleSelector}, }; -thread_local! { - pub(crate) static VIEW_CONTEXT_STORE: std::cell::RefCell = Default::default(); -} - -// Primarily used to mint and assign a unique ID to each view. -#[derive(Copy, Clone)] -pub struct ViewContext { - pub id: Id, -} - -impl ViewContext { - pub fn save() { - VIEW_CONTEXT_STORE.with(|store| { - store.borrow_mut().save(); - }) - } - - pub fn set_current(cx: ViewContext) { - VIEW_CONTEXT_STORE.with(|store| { - store.borrow_mut().set_current(cx); - }) - } - - pub fn get_current() -> ViewContext { - VIEW_CONTEXT_STORE.with(|store| store.borrow().cx) - } - - pub fn restore() { - VIEW_CONTEXT_STORE.with(|store| { - store.borrow_mut().restore(); - }) - } - - pub fn with_context(cx: ViewContext, f: impl FnOnce() -> T) -> T { - ViewContext::save(); - ViewContext::set_current(cx); - let value = f(); - ViewContext::restore(); - value - } - - /// Use this method if you are creating a `View` that has a child. - /// - /// Ensures that the child is initialized with the "correct" `ViewContext` - /// and that the context is restored after the child (and its children) are initialized. - /// For the child's `ViewContext` to be "correct", the child's ViewContext's `Id` must bet set to the parent `View`'s `Id`. - /// - /// This method returns the `Id` that should be attached to the parent `View` along with the initialized child. - pub fn new_id_with_child(child: impl FnOnce() -> V) -> (Id, V) { - let cx = ViewContext::get_current(); - let id = cx.new_id(); - let mut child_cx = cx; - child_cx.id = id; - let child = ViewContext::with_context(child_cx, child); - (id, child) - } - - pub fn with_id(mut self, id: Id) -> Self { - self.id = id; - self - } - - pub fn new_id(&self) -> Id { - self.id.new() - } -} - -pub(crate) struct ViewContextStore { - pub(crate) cx: ViewContext, - pub(crate) saved_cx: Vec, -} - -impl Default for ViewContextStore { - fn default() -> Self { - Self { - cx: ViewContext { id: Id::next() }, - saved_cx: Vec::new(), - } - } -} - -impl ViewContextStore { - pub(crate) fn save(&mut self) { - self.saved_cx.push(self.cx); - } - - pub(crate) fn set_current(&mut self, cx: ViewContext) { - self.cx = cx; - } - - pub(crate) fn restore(&mut self) { - if let Some(cx) = self.saved_cx.pop() { - self.cx = cx; - } - } -} - pub type EventCallback = dyn Fn(&Event) -> bool; pub type ResizeCallback = dyn Fn(Rect); pub type MenuCallback = dyn Fn() -> Menu; diff --git a/src/ext_event.rs b/src/ext_event.rs index a311a8cd..4615b56d 100644 --- a/src/ext_event.rs +++ b/src/ext_event.rs @@ -4,7 +4,11 @@ use floem_reactive::{create_effect, untrack, with_scope, ReadSignal, Scope, Trig use once_cell::sync::Lazy; use parking_lot::Mutex; -use crate::{app::UserEvent, Application}; +use crate::{ + app::UserEvent, + window_handle::{get_current_view, set_current_view}, + Application, +}; pub(crate) static EXT_EVENT_HANDLER: Lazy = Lazy::new(ExtEventHandler::default); @@ -34,6 +38,7 @@ pub fn create_ext_action( cx: Scope, action: impl Fn(T) + 'static, ) -> impl FnOnce(T) { + let view = get_current_view(); let cx = cx.create_child(); let trigger = cx.create_trigger(); let data = Arc::new(Mutex::new(None)); @@ -45,7 +50,10 @@ pub fn create_ext_action( trigger.track(); if let Some(event) = data.lock().take() { untrack(|| { + let current_view = get_current_view(); + set_current_view(view); action(event); + set_current_view(current_view); }); cx.dispose(); } diff --git a/src/id.rs b/src/id.rs index 0f010ad3..1c75c250 100644 --- a/src/id.rs +++ b/src/id.rs @@ -18,7 +18,7 @@ use crate::{ event::EventListener, responsive::ScreenSize, style::{Style, StyleSelector}, - update::{UpdateMessage, DEFERRED_UPDATE_MESSAGES, UPDATE_MESSAGES}, + update::{UpdateMessage, CENTRAL_DEFERRED_UPDATE_MESSAGES, CENTRAL_UPDATE_MESSAGES}, }; thread_local! { @@ -60,6 +60,15 @@ impl Id { new_id } + pub(crate) fn set_parent(&self, parent: Id) { + ID_PATHS.with(|id_paths| { + let mut id_paths = id_paths.borrow_mut(); + let mut id_path = id_paths.get(&parent).cloned().unwrap(); + id_path.0.push(*self); + id_paths.insert(*self, id_path); + }); + } + pub fn parent(&self) -> Option { ID_PATHS.with(|id_paths| { id_paths.borrow().get(self).and_then(|id_path| { @@ -119,23 +128,15 @@ impl Id { } pub fn update_state(&self, state: impl Any, deferred: bool) { - if let Some(root) = self.root_id() { - if !deferred { - UPDATE_MESSAGES.with(|msgs| { - let mut msgs = msgs.borrow_mut(); - let msgs = msgs.entry(root).or_default(); - msgs.push(UpdateMessage::State { - id: *self, - state: Box::new(state), - }) - }); - } else { - DEFERRED_UPDATE_MESSAGES.with(|msgs| { - let mut msgs = msgs.borrow_mut(); - let msgs = msgs.entry(root).or_default(); - msgs.push((*self, Box::new(state))); - }); - } + if !deferred { + self.add_update_message(UpdateMessage::State { + id: *self, + state: Box::new(state), + }); + } else { + CENTRAL_DEFERRED_UPDATE_MESSAGES.with(|msgs| { + msgs.borrow_mut().push((*self, Box::new(state))); + }); } } @@ -207,12 +208,8 @@ impl Id { } fn add_update_message(&self, msg: UpdateMessage) { - if let Some(root) = self.root_id() { - UPDATE_MESSAGES.with(|msgs| { - let mut msgs = msgs.borrow_mut(); - let msgs = msgs.entry(root).or_default(); - msgs.push(msg); - }); - } + CENTRAL_UPDATE_MESSAGES.with(|msgs| { + msgs.borrow_mut().push((*self, msg)); + }); } } diff --git a/src/lib.rs b/src/lib.rs index 1b5412c9..ddb55383 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,6 @@ pub mod window; mod window_handle; pub use app::{launch, quit_app, AppEvent, Application}; -pub use context::ViewContext; pub use floem_reactive as reactive; pub use floem_renderer::cosmic_text; pub use floem_renderer::Renderer; diff --git a/src/style.rs b/src/style.rs index 4733945e..e80d01b6 100644 --- a/src/style.rs +++ b/src/style.rs @@ -727,9 +727,8 @@ impl Style { self.display(Display::None) } - pub fn z_index(mut self, z_index: i32) -> Self { - self.z_index = Some(z_index).into(); - self + pub fn flex(self) -> Self { + self.display(Display::Flex) } pub fn flex_basis_px(self, pt: f32) -> Self { @@ -744,6 +743,11 @@ impl Style { self.flex_direction(FlexDirection::Column) } + pub fn z_index(mut self, z_index: i32) -> Self { + self.z_index = Some(z_index).into(); + self + } + /// Allow the application of a function if the option exists. /// This is useful for chaining together a bunch of optional style changes. /// ```rust,ignore diff --git a/src/update.rs b/src/update.rs index a68f7d1b..7cb4e891 100644 --- a/src/update.rs +++ b/src/update.rs @@ -13,9 +13,11 @@ use crate::{ }; thread_local! { + pub(crate) static CENTRAL_UPDATE_MESSAGES: RefCell> = Default::default(); /// Stores a queue of update messages for each view. This is a list of build in messages, including a built-in State message /// that you can use to send a state update to a view. pub(crate) static UPDATE_MESSAGES: RefCell>> = Default::default(); + pub(crate) static CENTRAL_DEFERRED_UPDATE_MESSAGES: RefCell)>> = Default::default(); pub(crate) static DEFERRED_UPDATE_MESSAGES: RefCell = Default::default(); pub(crate) static ANIM_UPDATE_MESSAGES: RefCell> = Default::default(); /// It stores the active view handle, so that when you dispatch an action, it knows @@ -84,7 +86,7 @@ pub(crate) enum UpdateMessage { action: Box, }, ToggleWindowMaximized, - HandleTitleBar(bool), + DragWindow, SetWindowDelta(Vec2), Animation { id: Id, diff --git a/src/view.rs b/src/view.rs index dcec9cc2..8b9f4dfc 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1053,6 +1053,14 @@ fn view_previous_sibling<'a>( None } +pub(crate) fn view_children_set_parent_id(view: &dyn View) { + let parent_id = view.id(); + for child in view.children() { + child.id().set_parent(parent_id); + view_children_set_parent_id(child); + } +} + fn view_nested_last_child(view: &dyn View) -> &dyn View { let mut last_child = view; while let Some(new_last_child) = last_child.children().pop() { diff --git a/src/view_tuple.rs b/src/view_tuple.rs index 24c85727..f5939d91 100644 --- a/src/view_tuple.rs +++ b/src/view_tuple.rs @@ -5,7 +5,9 @@ use crate::view::View; pub trait ViewTuple { fn paint(&mut self, cx: &mut PaintCx); - fn foreach bool>(&mut self, f: &mut F); + fn foreach(&self, f: F); + + fn foreach_mut bool>(&mut self, f: &mut F); fn foreach_rev bool>(&mut self, f: &mut F); @@ -22,7 +24,11 @@ macro_rules! impl_view_tuple { ( $n: tt; $( $t:ident),* ; $( $i:tt ),* ; $( $j:tt ),*) => { impl< $( $t: View, )* > ViewTuple for ( $( $t, )* ) { - fn foreach bool>(&mut self, f: &mut F) { + fn foreach(&self, f: F) { + $( f(&self.$i); )* + } + + fn foreach_mut bool>(&mut self, f: &mut F) { $( if f(&mut self.$i) { return; } )* } diff --git a/src/views/clip.rs b/src/views/clip.rs index 04c5b0be..71f5c698 100644 --- a/src/views/clip.rs +++ b/src/views/clip.rs @@ -1,7 +1,6 @@ use kurbo::{Rect, Size}; use crate::{ - context::ViewContext, id::Id, view::{ChangeFlags, View}, }; @@ -11,9 +10,11 @@ pub struct Clip { child: V, } -pub fn clip(child: impl FnOnce() -> V) -> Clip { - let (id, child) = ViewContext::new_id_with_child(child); - Clip { id, child } +pub fn clip(child: V) -> Clip { + Clip { + id: Id::next(), + child, + } } impl View for Clip { diff --git a/src/views/container.rs b/src/views/container.rs index 55613726..aca9b488 100644 --- a/src/views/container.rs +++ b/src/views/container.rs @@ -1,7 +1,6 @@ use kurbo::Rect; use crate::{ - context::ViewContext, id::Id, view::{ChangeFlags, View}, }; @@ -16,9 +15,11 @@ pub struct Container { /// /// A [`Container`] is useful for wrapping another [View](crate::view::View). This is often useful for allowing another /// set of styles completely separate from the View that is being wrapped. -pub fn container(child: impl FnOnce() -> V) -> Container { - let (id, child) = ViewContext::new_id_with_child(child); - Container { id, child } +pub fn container(child: V) -> Container { + Container { + id: Id::next(), + child, + } } impl View for Container { diff --git a/src/views/container_box.rs b/src/views/container_box.rs index f7f9f25a..fc84af4a 100644 --- a/src/views/container_box.rs +++ b/src/views/container_box.rs @@ -1,7 +1,6 @@ use kurbo::Rect; use crate::{ - context::ViewContext, id::Id, view::{ChangeFlags, View}, }; @@ -48,9 +47,11 @@ pub struct ContainerBox { /// } /// }); /// ``` -pub fn container_box(child: impl FnOnce() -> Box) -> ContainerBox { - let (id, child) = ViewContext::new_id_with_child(child); - ContainerBox { id, child } +pub fn container_box(child: impl View + 'static) -> ContainerBox { + ContainerBox { + id: Id::next(), + child: Box::new(child), + } } impl View for ContainerBox { diff --git a/src/views/double_click.rs b/src/views/double_click.rs deleted file mode 100644 index 3876a935..00000000 --- a/src/views/double_click.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::any::Any; - -use crate::{ - app_handle::ViewContext, - context::{EventCx, UpdateCx}, - event::Event, - id::Id, - view::{ChangeFlags, View}, -}; - -pub struct DoubleClick { - id: Id, - child: V, - on_double_click: Box, -} - -pub fn double_click( - cx: ViewContext, - child: impl FnOnce(ViewContext) -> V, - on_double_click: impl Fn() + 'static, -) -> DoubleClick { - let id = cx.new_id(); - let mut child_cx = cx; - child_cx.id = id; - let child = child(child_cx); - DoubleClick { - id, - child, - on_double_click: Box::new(on_double_click), - } -} - -impl View for DoubleClick { - fn id(&self) -> Id { - self.id - } - - fn child(&mut self, id: Id) -> Option<&mut dyn View> { - if self.child.id() == id { - Some(&mut self.child) - } else { - None - } - } - - fn children(&mut self) -> Vec<&mut dyn View> { - vec![&mut self.child] - } - - fn debug_name(&self) -> std::borrow::Cow<'static, str> { - "DoubleClick".into() - } - - fn update(&mut self, _cx: &mut UpdateCx, _state: Box) -> ChangeFlags { - ChangeFlags::empty() - } - - fn layout(&mut self, cx: &mut crate::context::LayoutCx) -> taffy::prelude::Node { - cx.layout_node(self.id, true, |cx| vec![self.child.layout_main(cx)]) - } - - fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) { - self.child.compute_layout_main(cx); - } - - fn event(&mut self, cx: &mut EventCx, id_path: Option<&[Id]>, event: Event) -> bool { - if id_path.is_none() { - // only send event to child if id_path is_none, - // because if id_path is_some, this event is destined to this view - if self.child.event_main(cx, id_path, event.clone()) { - return true; - } - } - - match &event { - Event::PointerDown(event) => { - if event.count == 2 { - cx.update_active(self.id); - true - } else { - false - } - } - Event::PointerUp(event) => { - let rect = cx.get_size(self.id).unwrap_or_default().to_rect(); - if rect.contains(event.pos) { - (self.on_double_click)(); - } - true - } - _ => false, - } - } - - fn paint(&mut self, cx: &mut crate::context::PaintCx) { - self.child.paint_main(cx); - } -} diff --git a/src/views/handle_titlebar_area.rs b/src/views/drag_window_area.rs similarity index 77% rename from src/views/handle_titlebar_area.rs rename to src/views/drag_window_area.rs index fcdc9921..0b2b5ad4 100644 --- a/src/views/handle_titlebar_area.rs +++ b/src/views/drag_window_area.rs @@ -1,8 +1,7 @@ use kurbo::Rect; use crate::{ - action::{set_handle_titlebar, toggle_window_maximized}, - context::ViewContext, + action::{drag_window, toggle_window_maximized}, event::{Event, EventListener}, id::Id, view::View, @@ -10,20 +9,16 @@ use crate::{ use super::Decorators; -pub struct HandleTitlebarArea { +pub struct DargWindowArea { id: Id, child: V, } -pub fn handle_titlebar_area(child: impl FnOnce() -> V) -> HandleTitlebarArea { - let (id, child) = ViewContext::new_id_with_child(child); - HandleTitlebarArea { id, child } +pub fn drag_window_area(child: V) -> DargWindowArea { + let id = Id::next(); + DargWindowArea { id, child } .on_event(EventListener::PointerDown, |_| { - set_handle_titlebar(true); - true - }) - .on_event(EventListener::PointerUp, |_| { - set_handle_titlebar(false); + drag_window(); true }) .on_double_click(|_| { @@ -32,7 +27,7 @@ pub fn handle_titlebar_area(child: impl FnOnce() -> V) -> HandleTitleba }) } -impl View for HandleTitlebarArea { +impl View for DargWindowArea { fn id(&self) -> Id { self.id } diff --git a/src/views/dyn_container.rs b/src/views/dyn_container.rs index d3f129f4..fbc5afb0 100644 --- a/src/views/dyn_container.rs +++ b/src/views/dyn_container.rs @@ -2,9 +2,8 @@ use floem_reactive::{as_child_of_current_scope, create_effect, Scope}; use kurbo::Rect; use crate::{ - context::ViewContext, id::Id, - view::{ChangeFlags, View}, + view::{view_children_set_parent_id, ChangeFlags, View}, }; type ChildFn = dyn Fn(T) -> (Box, Scope); @@ -15,7 +14,6 @@ pub struct DynamicContainer { child: Box, child_scope: Scope, child_fn: Box>, - cx: ViewContext, } /// A container for a dynamically updating View @@ -75,11 +73,7 @@ pub fn dyn_container Box + 'static, T: 'static>( update_view: impl Fn() -> T + 'static, child_fn: CF, ) -> DynamicContainer { - let cx = ViewContext::get_current(); - let id = cx.new_id(); - - let mut child_cx = cx; - child_cx.id = id; + let id = Id::next(); create_effect(move |_| { id.update_state(update_view(), false); @@ -91,7 +85,6 @@ pub fn dyn_container Box + 'static, T: 'static>( child: Box::new(crate::views::empty()), child_scope: Scope::new(), child_fn, - cx: child_cx, } } @@ -134,11 +127,11 @@ impl View for DynamicContainer { state: Box, ) -> crate::view::ChangeFlags { if let Ok(val) = state.downcast::() { - ViewContext::with_context(self.cx, || { - let old_child_scope = self.child_scope; - (self.child, self.child_scope) = (self.child_fn)(*val); - old_child_scope.dispose(); - }); + let old_child_scope = self.child_scope; + (self.child, self.child_scope) = (self.child_fn)(*val); + old_child_scope.dispose(); + self.child.id().set_parent(self.id); + view_children_set_parent_id(&*self.child); cx.request_layout(self.id()); ChangeFlags::LAYOUT } else { diff --git a/src/views/empty.rs b/src/views/empty.rs index c4d20704..33efe5a8 100644 --- a/src/views/empty.rs +++ b/src/views/empty.rs @@ -1,5 +1,4 @@ use crate::{ - context::ViewContext, id::Id, view::{ChangeFlags, View}, }; @@ -9,9 +8,7 @@ pub struct Empty { } pub fn empty() -> Empty { - let cx = ViewContext::get_current(); - let id = cx.new_id(); - Empty { id } + Empty { id: Id::next() } } impl View for Empty { diff --git a/src/views/label.rs b/src/views/label.rs index e625024b..05836e2c 100644 --- a/src/views/label.rs +++ b/src/views/label.rs @@ -1,7 +1,6 @@ use std::any::Any; use crate::{ - context::ViewContext, cosmic_text::{Attrs, AttrsList, FamilyOwned, TextLayout}, style::{ComputedStyle, TextOverflow}, }; @@ -39,9 +38,13 @@ pub struct Label { text_overflow: TextOverflow, } +pub fn text>(text: S) -> Label { + let text = Into::::into(text); + label(move || text.clone()) +} + pub fn label + 'static>(label: impl Fn() -> S + 'static) -> Label { - let cx = ViewContext::get_current(); - let id = cx.new_id(); + let id = Id::next(); create_effect(move |_| { let new_label = Into::::into(label()); id.update_state(new_label, false); diff --git a/src/views/list.rs b/src/views/list.rs index 7e094814..8c6226d3 100644 --- a/src/views/list.rs +++ b/src/views/list.rs @@ -9,9 +9,9 @@ use rustc_hash::FxHasher; use smallvec::SmallVec; use crate::{ - context::{AppState, EventCx, UpdateCx, ViewContext}, + context::{AppState, EventCx, UpdateCx}, id::Id, - view::{ChangeFlags, View}, + view::{view_children_set_parent_id, ChangeFlags, View}, }; pub(crate) type FxIndexSet = indexmap::IndexSet>; @@ -29,7 +29,6 @@ where children: Vec>, view_fn: Box (V, Scope)>, phantom: PhantomData, - cx: ViewContext, } pub fn list(each_fn: IF, key_fn: KF, view_fn: VF) -> List @@ -42,11 +41,7 @@ where V: View + 'static, T: 'static, { - let cx = ViewContext::get_current(); - let id = cx.new_id(); - - let mut child_cx = cx; - child_cx.id = id; + let id = Id::next(); create_effect(move |prev_hash_run| { let items = each_fn(); let items = items.into_iter().collect::>(); @@ -80,7 +75,6 @@ where children: Vec::new(), view_fn, phantom: PhantomData, - cx: child_cx, } } @@ -139,9 +133,13 @@ impl View for List { state: Box, ) -> crate::view::ChangeFlags { if let Ok(diff) = state.downcast() { - ViewContext::with_context(self.cx, || { - apply_diff(cx.app_state, *diff, &mut self.children, &self.view_fn); - }); + apply_diff( + self.id, + cx.app_state, + *diff, + &mut self.children, + &self.view_fn, + ); cx.request_layout(self.id()); ChangeFlags::LAYOUT } else { @@ -333,6 +331,7 @@ fn remove_index( } pub(super) fn apply_diff( + view_id: Id, app_state: &mut AppState, mut diff: Diff, children: &mut Vec>, @@ -377,6 +376,10 @@ pub(super) fn apply_diff( for DiffOpAdd { at, view } in diff.added { children[at] = view.map(view_fn); + if let Some((child, _)) = children[at].as_ref() { + child.id().set_parent(view_id); + view_children_set_parent_id(child); + } } for (to, each_item) in items_to_move { diff --git a/src/views/mod.rs b/src/views/mod.rs index c01cd8a1..5f940044 100644 --- a/src/views/mod.rs +++ b/src/views/mod.rs @@ -48,5 +48,5 @@ pub use text_input::*; mod empty; pub use empty::*; -mod handle_titlebar_area; -pub use handle_titlebar_area::*; +mod drag_window_area; +pub use drag_window_area::*; diff --git a/src/views/rich_text.rs b/src/views/rich_text.rs index 371fa1fb..a10c4409 100644 --- a/src/views/rich_text.rs +++ b/src/views/rich_text.rs @@ -6,7 +6,7 @@ use kurbo::{Point, Rect}; use taffy::{prelude::Node, style::Dimension}; use crate::{ - context::{EventCx, UpdateCx, ViewContext}, + context::{EventCx, UpdateCx}, event::Event, id::Id, style::{ComputedStyle, Style, TextOverflow}, @@ -22,8 +22,7 @@ pub struct RichText { } pub fn rich_text(text_layout: impl Fn() -> TextLayout + 'static) -> RichText { - let cx = ViewContext::get_current(); - let id = cx.new_id(); + let id = Id::next(); let text = text_layout(); create_effect(move |_| { let new_text_layout = text_layout(); diff --git a/src/views/scroll.rs b/src/views/scroll.rs index 244f2c79..c6d57d9c 100644 --- a/src/views/scroll.rs +++ b/src/views/scroll.rs @@ -8,7 +8,7 @@ use taffy::{ }; use crate::{ - context::{AppState, LayoutCx, PaintCx, ViewContext}, + context::{AppState, LayoutCx, PaintCx}, event::Event, id::Id, style::{ComputedStyle, Style, StyleValue}, @@ -93,10 +93,9 @@ pub struct Scroll { scroll_bar_style: ScrollBarStyle, } -pub fn scroll(child: impl FnOnce() -> V) -> Scroll { - let (id, child) = ViewContext::new_id_with_child(child); +pub fn scroll(child: V) -> Scroll { Scroll { - id, + id: Id::next(), child, size: Size::ZERO, actual_rect: Rect::ZERO, diff --git a/src/views/stack.rs b/src/views/stack.rs index e541029c..52a784e5 100644 --- a/src/views/stack.rs +++ b/src/views/stack.rs @@ -1,7 +1,7 @@ use kurbo::Rect; use crate::{ - context::{EventCx, UpdateCx, ViewContext}, + context::{EventCx, UpdateCx}, id::Id, view::{ChangeFlags, View}, view_tuple::ViewTuple, @@ -12,8 +12,8 @@ pub struct Stack { children: VT, } -pub fn stack(children: impl FnOnce() -> VT) -> Stack { - let (id, children) = ViewContext::new_id_with_child(children); +pub fn stack(children: VT) -> Stack { + let id = Id::next(); Stack { id, children } } @@ -75,7 +75,7 @@ impl View for Stack { fn layout(&mut self, cx: &mut crate::context::LayoutCx) -> taffy::prelude::Node { cx.layout_node(self.id, true, |cx| { let mut nodes = Vec::new(); - self.children.foreach(&mut |view| { + self.children.foreach_mut(&mut |view| { let node = view.layout_main(cx); nodes.push(node); false @@ -86,7 +86,7 @@ impl View for Stack { fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { let mut layout_rect = Rect::ZERO; - self.children.foreach(&mut |view| { + self.children.foreach_mut(&mut |view| { layout_rect = layout_rect.union(view.compute_layout_main(cx)); false }); @@ -94,7 +94,7 @@ impl View for Stack { } fn paint(&mut self, cx: &mut crate::context::PaintCx) { - self.children.foreach(&mut |view| { + self.children.foreach_mut(&mut |view| { view.paint_main(cx); false }); diff --git a/src/views/svg.rs b/src/views/svg.rs index a05a823b..95cebcc5 100644 --- a/src/views/svg.rs +++ b/src/views/svg.rs @@ -9,7 +9,6 @@ use peniko::Color; use sha2::{Digest, Sha256}; use crate::{ - context::ViewContext, id::Id, view::{ChangeFlags, View}, views::Decorators, @@ -22,8 +21,7 @@ pub struct Svg { } pub fn svg(svg_str: impl Fn() -> String + 'static) -> Svg { - let cx = ViewContext::get_current(); - let id = cx.new_id(); + let id = Id::next(); create_effect(move |_| { let new_svg_str = svg_str(); id.update_state(new_svg_str, false); diff --git a/src/views/tab.rs b/src/views/tab.rs index 1f32d9c7..6418e999 100644 --- a/src/views/tab.rs +++ b/src/views/tab.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use taffy::style::Display; use crate::{ - context::{EventCx, UpdateCx, ViewContext}, + context::{EventCx, UpdateCx}, id::Id, view::{ChangeFlags, View}, }; @@ -28,7 +28,6 @@ where children: Vec>, view_fn: Box (V, Scope)>, phatom: PhantomData, - cx: ViewContext, } pub fn tab( @@ -46,11 +45,7 @@ where V: View + 'static, T: 'static, { - let cx = ViewContext::get_current(); - let id = cx.new_id(); - - let mut child_cx = cx; - child_cx.id = id; + let id = Id::next(); create_effect(move |prev_hash_run| { let items = each_fn(); @@ -93,7 +88,6 @@ where children: Vec::new(), view_fn, phatom: PhantomData, - cx: child_cx, } } @@ -154,9 +148,13 @@ impl View for Tab { if let Ok(state) = state.downcast::>() { match *state { TabState::Diff(diff) => { - ViewContext::with_context(self.cx, || { - apply_diff(cx.app_state, *diff, &mut self.children, &self.view_fn); - }); + apply_diff( + self.id, + cx.app_state, + *diff, + &mut self.children, + &self.view_fn, + ); } TabState::Active(active) => { self.active = active; diff --git a/src/views/text_input.rs b/src/views/text_input.rs index 00c3188c..912aa146 100644 --- a/src/views/text_input.rs +++ b/src/views/text_input.rs @@ -1,5 +1,4 @@ use crate::action::exec_after; -use crate::context::ViewContext; use crate::keyboard::KeyEvent; use crate::reactive::{create_effect, RwSignal}; use crate::{context::LayoutCx, style::CursorStyle}; @@ -97,8 +96,7 @@ pub enum Direction { /// Text Input View pub fn text_input(buffer: RwSignal) -> TextInput { - let cx = ViewContext::get_current(); - let id = cx.new_id(); + let id = Id::next(); { create_effect(move |_| { diff --git a/src/views/virtual_list.rs b/src/views/virtual_list.rs index de022b96..00a54074 100644 --- a/src/views/virtual_list.rs +++ b/src/views/virtual_list.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use taffy::{prelude::Node, style::Dimension}; use crate::{ - context::{LayoutCx, ViewContext}, + context::LayoutCx, id::Id, view::{ChangeFlags, View}, }; @@ -51,7 +51,6 @@ where set_viewport: WriteSignal, view_fn: Box (V, Scope)>, phatom: PhantomData, - cx: ViewContext, before_size: f64, after_size: f64, before_node: Option, @@ -80,11 +79,7 @@ where VF: Fn(T) -> V + 'static, V: View + 'static, { - let cx = ViewContext::get_current(); - let id = cx.new_id(); - - let mut child_cx = cx; - child_cx.id = id; + let id = Id::next(); let (viewport, set_viewport) = create_signal(Rect::ZERO); @@ -193,7 +188,6 @@ where set_viewport, view_fn, phatom: PhantomData, - cx: child_cx, before_size: 0.0, after_size: 0.0, before_node: None, @@ -264,9 +258,13 @@ impl View for VirtualList { } self.before_size = state.before_size; self.after_size = state.after_size; - ViewContext::with_context(self.cx, || { - apply_diff(cx.app_state, state.diff, &mut self.children, &self.view_fn); - }); + apply_diff( + self.id, + cx.app_state, + state.diff, + &mut self.children, + &self.view_fn, + ); cx.request_layout(self.id()); ChangeFlags::LAYOUT } else { diff --git a/src/window_handle.rs b/src/window_handle.rs index 62815262..ceb5b5d0 100644 --- a/src/window_handle.rs +++ b/src/window_handle.rs @@ -20,19 +20,19 @@ use crate::{ animate::{AnimPropKind, AnimUpdateMsg, AnimValue, AnimatedProp, SizeUnit}, context::{ AppState, EventCx, LayoutCx, MoveListener, PaintCx, PaintState, ResizeListener, UpdateCx, - ViewContext, }, event::{Event, EventListener}, - id::{Id, ID_PATHS}, + id::{Id, IdPath, ID_PATHS}, keyboard::KeyEvent, menu::Menu, pointer::{PointerButton, PointerInputEvent, PointerMoveEvent, PointerWheelEvent}, style::{CursorStyle, StyleSelector}, update::{ - UpdateMessage, ANIM_UPDATE_MESSAGES, CURRENT_RUNNING_VIEW_HANDLE, DEFERRED_UPDATE_MESSAGES, + UpdateMessage, ANIM_UPDATE_MESSAGES, CENTRAL_DEFERRED_UPDATE_MESSAGES, + CENTRAL_UPDATE_MESSAGES, CURRENT_RUNNING_VIEW_HANDLE, DEFERRED_UPDATE_MESSAGES, UPDATE_MESSAGES, }, - view::{ChangeFlags, View}, + view::{view_children_set_parent_id, ChangeFlags, View}, }; /// The top-level window handle that owns the winit Window. @@ -45,7 +45,7 @@ pub(crate) struct WindowHandle { pub(crate) window: Option, /// Reactive Scope for this WindowHandle scope: Scope, - pub(crate) view: Box, + view: Box, app_state: AppState, paint_state: PaintState, size: RwSignal, @@ -63,12 +63,8 @@ impl WindowHandle { window: winit::window::Window, view_fn: impl FnOnce(winit::window::WindowId) -> Box + 'static, ) -> Self { - let window_id = window.id(); - let id = Id::next(); - set_current_view(id); - let scope = Scope::new(); - + let window_id = window.id(); let scale = window.scale_factor(); let size: LogicalSize = window.inner_size().to_logical(scale); let size = Size::new(size.width, size.height); @@ -76,27 +72,28 @@ impl WindowHandle { #[cfg(target_os = "linux")] let context_menu = scope.create_rw_signal(None); - let cx = ViewContext { id }; #[cfg(not(target_os = "linux"))] - let view = ViewContext::with_context(cx, || with_scope(scope, move || view_fn(window_id))); + let view = with_scope(scope, move || view_fn(window_id)); #[cfg(target_os = "linux")] - let view = ViewContext::with_context(cx, || { - with_scope(scope, move || { - Box::new( - stack(|| { - ( - container_box(move || view_fn(window_id)) - .style(|s| s.size_pct(100.0, 100.0)), - context_menu_view(scope, window_id, context_menu, size), - ) - }) - .style(|s| s.size_pct(100.0, 100.0)), - ) - }) + let view = with_scope(scope, move || { + Box::new( + stack(( + container_box(view_fn(window_id)).style(|s| s.size_pct(100.0, 100.0)), + context_menu_view(scope, window_id, context_menu, size), + )) + .style(|s| s.size_pct(100.0, 100.0)), + ) }); + ID_PATHS.with(|id_paths| { + id_paths + .borrow_mut() + .insert(view.id(), IdPath(vec![view.id()])); + }); + view_children_set_parent_id(&*view); + let paint_state = PaintState::new(&window, scale, size.get_untracked() * scale); let mut window_handle = Self { window: Some(window), @@ -452,9 +449,42 @@ impl WindowHandle { } } + fn process_central_messages(&self) { + CENTRAL_UPDATE_MESSAGES.with(|central_msgs| { + if !central_msgs.borrow().is_empty() { + UPDATE_MESSAGES.with(|msgs| { + let mut msgs = msgs.borrow_mut(); + let central_msgs = std::mem::take(&mut *central_msgs.borrow_mut()); + for (id, msg) in central_msgs { + if let Some(root) = id.root_id() { + let msgs = msgs.entry(root).or_default(); + msgs.push(msg); + } + } + }); + } + }); + + CENTRAL_DEFERRED_UPDATE_MESSAGES.with(|central_msgs| { + if !central_msgs.borrow().is_empty() { + DEFERRED_UPDATE_MESSAGES.with(|msgs| { + let mut msgs = msgs.borrow_mut(); + let central_msgs = std::mem::take(&mut *central_msgs.borrow_mut()); + for (id, msg) in central_msgs { + if let Some(root) = id.root_id() { + let msgs = msgs.entry(root).or_default(); + msgs.push((id, msg)); + } + } + }); + } + }); + } + fn process_update_messages(&mut self) -> ChangeFlags { let mut flags = ChangeFlags::empty(); loop { + self.process_central_messages(); let msgs = UPDATE_MESSAGES.with(|msgs| { msgs.borrow_mut() .remove(&self.view.id()) @@ -552,27 +582,23 @@ impl WindowHandle { UpdateMessage::Draggable { id } => { cx.app_state.draggable.insert(id); } - UpdateMessage::HandleTitleBar(_val) => { + UpdateMessage::DragWindow => { if let Some(window) = self.window.as_ref() { let _ = window.drag_window(); } } UpdateMessage::ToggleWindowMaximized => { - // let window_state = self.handle.get_window_state(); - // match window_state { - // glazier::WindowState::Maximized => { - // self.handle.set_window_state(WindowState::Restored); - // } - // glazier::WindowState::Minimized => { - // self.handle.set_window_state(WindowState::Maximized); - // } - // glazier::WindowState::Restored => { - // self.handle.set_window_state(WindowState::Maximized); - // } - // } + if let Some(window) = self.window.as_ref() { + window.set_maximized(!window.is_maximized()); + } } - UpdateMessage::SetWindowDelta(_delta) => { - // self.handle.set_position(self.handle.get_position() + delta); + UpdateMessage::SetWindowDelta(delta) => { + if let Some(window) = self.window.as_ref() { + let pos = self.window_position + delta; + window.set_outer_position(winit::dpi::Position::Logical( + winit::dpi::LogicalPosition::new(pos.x, pos.y), + )); + } } UpdateMessage::EventListener { id, @@ -667,8 +693,8 @@ impl WindowHandle { } fn process_deferred_update_messages(&mut self) -> ChangeFlags { + self.process_central_messages(); let mut flags = ChangeFlags::empty(); - let msgs = DEFERRED_UPDATE_MESSAGES.with(|msgs| { msgs.borrow_mut() .remove(&self.view.id()) @@ -976,7 +1002,7 @@ fn context_menu_view( move |s| s.clone(), move |s| { if let Some((id, enabled, s)) = s { - container_box(|| Box::new(label(move || s.clone()))) + container_box(label(move || s.clone())) .on_click(move |_| { context_menu.set(None); if let Some(id) = id { @@ -1003,14 +1029,12 @@ fn context_menu_view( .active_style(|s| s.border_radius(10.0).background(Color::rgb8(92, 92, 92))) .disabled_style(|s| s.color(Color::rgb8(92, 92, 92))) } else { - container_box(|| { - Box::new(empty().style(|s| { - s.width_pct(100.0) - .height_px(1.0) - .margin_vert_px(5.0) - .background(Color::rgb8(92, 92, 92)) - })) - }) + container_box(empty().style(|s| { + s.width_pct(100.0) + .height_px(1.0) + .margin_vert_px(5.0) + .background(Color::rgb8(92, 92, 92)) + })) .style(|s| s.min_width_pct(100.0).padding_horiz_px(20.0)) } },