diff --git a/Cargo.toml b/Cargo.toml index d3685d85..138a933d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ rust-version = "1.75" license.workspace = true [workspace.dependencies] +peniko = "0.1.0" +kurbo = { version = "0.11", features = ["serde"] } serde = "1.0" lapce-xi-rope = { version = "0.3.2", features = ["serde"] } strum = { version = "0.26.2" } @@ -31,6 +33,7 @@ raw-window-handle = "0.6.0" wgpu = { version = "0.19.0" } [dependencies] +slotmap = "1.0.7" sha2 = "0.10.6" bitflags = "2.2.1" indexmap = "2" @@ -42,9 +45,9 @@ rfd = { version = "0.14.0", default-features = false, features = [ "xdg-portal", ], optional = true } raw-window-handle = { workspace = true } -kurbo = { version = "0.9.5", features = ["serde"] } unicode-segmentation = "1.10.0" -floem-peniko = "0.1.0" +peniko = { workspace = true } +kurbo = { workspace = true } crossbeam-channel = "0.5.6" im-rc = "15.1.0" serde = { workspace = true, optional = true } diff --git a/README.md b/README.md index d74b6ba7..8a7c3e76 100644 --- a/README.md +++ b/README.md @@ -17,29 +17,30 @@ _The project is still maturing. We will make occasional breaking changes and add ![Quickstart](docs/img/quickstart.png) ```rust -use floem::reactive::create_signal; -use floem::view::View; -use floem::views::{h_stack, label, v_stack, Decorators}; -use floem::widgets::button; +use floem::{ + reactive::create_signal, + views::{label, ButtonClass, Decorators}, + IntoView, +}; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { // Create a reactive signal with a counter value, defaulting to 0 let (counter, set_counter) = create_signal(0); // Create a vertical layout - v_stack(( + ( // The counter value updates automatically, thanks to reactivity label(move || format!("Value: {}", counter.get())), // Create a horizontal layout - h_stack(( - button(|| "Increment").on_click_stop(move |_| { + ( + "Increment".class(ButtonClass).on_click_stop(move |_| { set_counter.update(|value| *value += 1); }), - button(|| "Decrement").on_click_stop(move |_| { + "Decrement".class(ButtonClass).on_click_stop(move |_| { set_counter.update(|value| *value -= 1); }), - )), - )) + ), + ).style(|s| s.flex_col()) } fn main() { diff --git a/editor-core/src/buffer/mod.rs b/editor-core/src/buffer/mod.rs index 9d330f09..c1c94933 100644 --- a/editor-core/src/buffer/mod.rs +++ b/editor-core/src/buffer/mod.rs @@ -2,6 +2,7 @@ use std::{ borrow::{Borrow, Cow}, cmp::Ordering, collections::BTreeSet, + fmt::Display, sync::{ atomic::{self, AtomicU64}, Arc, @@ -92,9 +93,9 @@ pub struct Buffer { line_ending: LineEnding, } -impl ToString for Buffer { - fn to_string(&self) -> String { - self.text().to_string() +impl Display for Buffer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.text().to_string()) } } diff --git a/examples/animations/src/main.rs b/examples/animations/src/main.rs index ae489f4d..27f7ec4b 100644 --- a/examples/animations/src/main.rs +++ b/examples/animations/src/main.rs @@ -6,16 +6,16 @@ use floem::{ peniko::Color, reactive::{create_rw_signal, create_signal}, style_class, - view::View, views::{container, empty, h_stack, label, stack, static_label, text, v_stack, Decorators}, + IntoView, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { v_stack((progress_bar_container(), cube_container())) } style_class!(pub Button); -fn progress_bar_container() -> impl View { +fn progress_bar_container() -> impl IntoView { let width = 300.0; let anim_id = create_rw_signal(None); let is_stopped = create_rw_signal(false); @@ -108,7 +108,7 @@ fn progress_bar_container() -> impl View { }) } -fn cube_container() -> impl View { +fn cube_container() -> impl IntoView { let (counter, set_counter) = create_signal(0.0); let (is_hovered, set_is_hovered) = create_signal(false); diff --git a/examples/context/src/main.rs b/examples/context/src/main.rs index 39d736e6..dd149a9f 100644 --- a/examples/context/src/main.rs +++ b/examples/context/src/main.rs @@ -2,20 +2,20 @@ use floem::{ keyboard::{Key, Modifiers, NamedKey}, peniko::Color, reactive::{provide_context, use_context}, - view::View, views::{empty, label, v_stack, Decorators}, + IntoView, View, }; -fn colored_label(text: String) -> impl View { +fn colored_label(text: String) -> impl IntoView { let color: Color = use_context().unwrap(); label(move || text.clone()).style(move |s| s.color(color)) } -fn context_container( +fn context_container( color: Color, name: String, view_fn: impl Fn() -> V, -) -> impl View { +) -> impl IntoView { provide_context(color); v_stack((colored_label(name), view_fn())).style(move |s| { @@ -27,7 +27,7 @@ fn context_container( }) } -fn app_view() -> impl View { +fn app_view() -> impl IntoView { provide_context(Color::BLACK); let view = v_stack(( diff --git a/examples/counter-simple/Cargo.toml b/examples/counter-simple/Cargo.toml new file mode 100644 index 00000000..395e20db --- /dev/null +++ b/examples/counter-simple/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "counter-simple" +version = "0.1.0" +edition = "2021" + +[dependencies] +im.workspace = true +floem = { path = "../.." } diff --git a/examples/counter-simple/src/main.rs b/examples/counter-simple/src/main.rs new file mode 100644 index 00000000..0c723493 --- /dev/null +++ b/examples/counter-simple/src/main.rs @@ -0,0 +1,30 @@ +use floem::{ + reactive::create_signal, + views::{label, ButtonClass, Decorators}, + IntoView, +}; + +fn app_view() -> impl IntoView { + // Create a reactive signal with a counter value, defaulting to 0 + let (counter, set_counter) = create_signal(0); + + // Create a vertical layout + ( + // The counter value updates automatically, thanks to reactivity + label(move || format!("Value: {}", counter.get())), + // Create a horizontal layout + ( + "Increment".class(ButtonClass).on_click_stop(move |_| { + set_counter.update(|value| *value += 1); + }), + "Decrement".class(ButtonClass).on_click_stop(move |_| { + set_counter.update(|value| *value -= 1); + }), + ), + ) + .style(|s| s.flex_col()) +} + +fn main() { + floem::launch(app_view); +} diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index bae19793..7e6da48d 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -3,16 +3,17 @@ use floem::{ peniko::Color, reactive::create_signal, unit::UnitExt, - view::View, - views::{label, stack, text, Decorators}, + views::{dyn_container, Decorators}, + IntoView, View, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let (counter, set_counter) = create_signal(0); - let view = stack(( - label(move || format!("Value: {}", counter.get())).style(|s| s.padding(10.0)), - stack(( - text("Increment") + let view = ( + dyn_container(move || format!("Value: {}", counter.get())), + counter.style(|s| s.padding(10.0)), + ( + "Increment" .style(|s| { s.border_radius(10.0) .padding(10.0) @@ -28,7 +29,7 @@ fn app_view() -> impl View { } }) .keyboard_navigatable(), - text("Decrement") + "Decrement" .on_click_stop({ move |_| { set_counter.update(|value| *value -= 1); @@ -45,7 +46,7 @@ fn app_view() -> impl View { .active(|s| s.color(Color::WHITE).background(Color::RED)) }) .keyboard_navigatable(), - text("Reset to 0") + "Reset to 0" .on_click_stop(move |_| { println!("Reset counter pressed"); // will not fire if button is disabled set_counter.update(|value| *value = 0); @@ -63,14 +64,14 @@ fn app_view() -> impl View { .active(|s| s.color(Color::WHITE).background(Color::YELLOW_GREEN)) }) .keyboard_navigatable(), - )), - )) - .style(|s| { - s.size(100.pct(), 100.pct()) - .flex_col() - .items_center() - .justify_center() - }); + ), + ) + .style(|s| { + s.size(100.pct(), 100.pct()) + .flex_col() + .items_center() + .justify_center() + }); let id = view.id(); view.on_key_up(Key::Named(NamedKey::F11), Modifiers::empty(), move |_| { diff --git a/examples/draggable/src/main.rs b/examples/draggable/src/main.rs index 8580866c..d57f0038 100644 --- a/examples/draggable/src/main.rs +++ b/examples/draggable/src/main.rs @@ -1,10 +1,10 @@ use floem::{ peniko::Color, - view::View, views::{label, Decorators}, + IntoView, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { label(|| "Drag me!") .style(|s| { s.border(1.0) diff --git a/examples/dyn-container/src/main.rs b/examples/dyn-container/src/main.rs index 26947466..9a828caa 100644 --- a/examples/dyn-container/src/main.rs +++ b/examples/dyn-container/src/main.rs @@ -1,8 +1,7 @@ use floem::{ reactive::{create_rw_signal, RwSignal}, - view::View, - views::{dyn_container, h_stack, label, v_stack, Decorators}, - widgets::button, + views::{button, dyn_container, h_stack, label, v_stack, Decorators}, + IntoView, }; #[derive(Clone, PartialEq)] @@ -11,11 +10,11 @@ enum ViewSwitcher { Two, } -fn view_one() -> impl View { +fn view_one() -> impl IntoView { label(|| "A view") } -fn view_two(view: RwSignal) -> impl View { +fn view_two(view: RwSignal) -> impl IntoView { v_stack(( label(|| "Another view"), button(|| "Switch back").on_click_stop(move |_| { @@ -25,7 +24,7 @@ fn view_two(view: RwSignal) -> impl View { .style(|s| s.gap(0.0, 10.0)) } -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let view = create_rw_signal(ViewSwitcher::One); v_stack(( @@ -41,13 +40,10 @@ fn app_view() -> impl View { }) .style(|s| s.margin_bottom(20)), )), - dyn_container( - move || view.get(), - move |value| match value { - ViewSwitcher::One => view_one().any(), - ViewSwitcher::Two => view_two(view).any(), - }, - ) + dyn_container(move || match view.get() { + ViewSwitcher::One => view_one().into_any(), + ViewSwitcher::Two => view_two(view).into_any(), + }) .style(|s| s.padding(10).border(1)), )) .style(|s| { diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index c1e32cc0..fc0e5232 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -1,8 +1,8 @@ use floem::{ keyboard::{Key, Modifiers, NamedKey}, reactive::RwSignal, - view::View, views::{ + button, editor::{ command::{Command, CommandExecuted}, core::{command::EditCommand, editor::EditType, selection::Selection}, @@ -10,10 +10,10 @@ use floem::{ }, stack, text_editor, Decorators, }, - widgets::button, + IntoView, View, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let text = std::env::args() .nth(1) .map(|s| std::fs::read_to_string(s).unwrap()); diff --git a/examples/files/src/main.rs b/examples/files/src/main.rs index de9dbeaf..7c913467 100644 --- a/examples/files/src/main.rs +++ b/examples/files/src/main.rs @@ -2,12 +2,11 @@ use floem::{ action::{open_file, save_as}, file::{FileDialogOptions, FileSpec}, keyboard::{Key, Modifiers, NamedKey}, - view::View, - views::{h_stack, Decorators}, - widgets::button, + views::{button, h_stack, Decorators}, + IntoView, View, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let view = h_stack(( button(|| "Select file").on_click_cont(|_| { open_file( diff --git a/examples/flight_booker/src/main.rs b/examples/flight_booker/src/main.rs index 3fd710ab..6e434e65 100644 --- a/examples/flight_booker/src/main.rs +++ b/examples/flight_booker/src/main.rs @@ -2,9 +2,11 @@ use floem::{ peniko::Color, reactive::{create_rw_signal, create_signal}, unit::UnitExt, - view::View, - views::{dyn_container, empty, h_stack, text, v_stack, Decorators}, - widgets::{button, labeled_radio_button, text_input}, + views::{ + button, dyn_container, empty, h_stack, labeled_radio_button, text, text_input, v_stack, + Decorators, + }, + IntoView, }; use time::Date; @@ -25,7 +27,7 @@ enum FlightMode { static DATE_FORMAT: &[time::format_description::FormatItem<'_>] = time::macros::format_description!("[day]-[month]-[year]"); -pub fn app_view() -> impl View { +pub fn app_view() -> impl IntoView { let (flight_mode, flight_mode_set) = create_signal(FlightMode::OneWay); let start_text = create_rw_signal("24-02-2024".to_string()); @@ -82,16 +84,13 @@ pub fn app_view() -> impl View { }) .on_click_stop(move |_| did_booking.set(true)); - let success_message = dyn_container( - move || did_booking.get(), - move |booked| match (booked, flight_mode.get()) { - (true, FlightMode::OneWay) => text(oneway_message(start_text.get())).any(), - (true, FlightMode::Return) => { - text(return_message(start_text.get(), return_text.get())).any() - } - (false, _) => empty().any(), - }, - ); + let success_message = dyn_container(move || match (did_booking.get(), flight_mode.get()) { + (true, FlightMode::OneWay) => text(oneway_message(start_text.get())).into_any(), + (true, FlightMode::Return) => { + text(return_message(start_text.get(), return_text.get())).into_any() + } + (false, _) => empty().into_any(), + }); v_stack(( mode_picker, diff --git a/examples/keyboard_handler/src/main.rs b/examples/keyboard_handler/src/main.rs index bd4e4d86..955e236d 100644 --- a/examples/keyboard_handler/src/main.rs +++ b/examples/keyboard_handler/src/main.rs @@ -2,11 +2,11 @@ use floem::{ event::{Event, EventListener}, keyboard::Key, unit::UnitExt, - view::View, views::{stack, text, Decorators}, + IntoView, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let view = stack((text("Example: Keyboard event handler").style(|s| s.padding(10.0)),)).style(|s| { s.size(100.pct(), 100.pct()) diff --git a/examples/keyboard_listener/Cargo.toml b/examples/keyboard_listener/Cargo.toml deleted file mode 100644 index 46321260..00000000 --- a/examples/keyboard_listener/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "keyboard_listener" -edition = "2021" -license.workspace = true -version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -floem = { path = "../.." } diff --git a/examples/keyboard_listener/src/main.rs b/examples/keyboard_listener/src/main.rs deleted file mode 100644 index a0324188..00000000 --- a/examples/keyboard_listener/src/main.rs +++ /dev/null @@ -1,31 +0,0 @@ -use floem::event::{Event, EventListener}; -use floem::reactive::create_rw_signal; -use floem::views::label; -use floem::{ - view::View, - views::{v_stack, Decorators}, - widgets::text_input, - EventPropagation, -}; - -fn app_view() -> impl View { - let text = create_rw_signal("".to_string()); - let keyboard_signal = create_rw_signal("".to_string()); - v_stack(( - text_input(text) - .placeholder("Write here") - .keyboard_navigatable(), - label(move || format!("Key Pressed: {}", keyboard_signal.get())) - .keyboard_listenable() - .on_event(EventListener::KeyDown, move |e| { - if let Event::KeyDown(e) = e { - keyboard_signal.set(e.key.logical_key.to_text().unwrap().to_string()); - } - EventPropagation::Continue - }), - )) -} - -fn main() { - floem::launch(app_view); -} diff --git a/examples/layout/src/draggable_sidebar.rs b/examples/layout/src/draggable_sidebar.rs index dfbe71b3..25111c7f 100644 --- a/examples/layout/src/draggable_sidebar.rs +++ b/examples/layout/src/draggable_sidebar.rs @@ -1,19 +1,18 @@ use floem::{ - event::{Event, EventListener}, + event::{Event, EventListener, EventPropagation}, peniko::Color, reactive::{create_rw_signal, create_signal}, style::{CursorStyle, Position}, - view::View, views::{ container, h_stack, label, scroll, virtual_stack, Decorators, VirtualDirection, VirtualItemSize, }, - EventPropagation, + IntoView, View, }; const SIDEBAR_WIDTH: f64 = 100.0; -pub fn draggable_sidebar_view() -> impl View { +pub fn draggable_sidebar_view() -> impl IntoView { let long_list: im::Vector = (0..100).collect(); let (long_list, _set_long_list) = create_signal(long_list); let sidebar_width = create_rw_signal(SIDEBAR_WIDTH); diff --git a/examples/layout/src/holy_grail.rs b/examples/layout/src/holy_grail.rs index bdfa3763..350432d0 100644 --- a/examples/layout/src/holy_grail.rs +++ b/examples/layout/src/holy_grail.rs @@ -3,18 +3,18 @@ use floem::{ peniko::Color, reactive::create_rw_signal, style::Position, - view::View, views::{ container, h_stack, label, scroll, v_stack, virtual_stack, Decorators, VirtualDirection, VirtualItemSize, }, + IntoView, View, }; const SIDEBAR_WIDTH: f64 = 140.0; const TOPBAR_HEIGHT: f64 = 30.0; const SIDEBAR_ITEM_HEIGHT: f64 = 21.0; -pub fn holy_grail_view() -> impl View { +pub fn holy_grail_view() -> impl IntoView { let long_list: im::Vector = (0..100).collect(); let long_list = create_rw_signal(long_list); diff --git a/examples/layout/src/left_sidebar.rs b/examples/layout/src/left_sidebar.rs index 05e06977..2c494466 100644 --- a/examples/layout/src/left_sidebar.rs +++ b/examples/layout/src/left_sidebar.rs @@ -3,18 +3,18 @@ use floem::{ peniko::Color, reactive::create_rw_signal, style::Position, - view::View, views::{ container, h_stack, label, scroll, v_stack, virtual_stack, Decorators, VirtualDirection, VirtualItemSize, }, + IntoView, View, }; const SIDEBAR_WIDTH: f64 = 140.0; const TOPBAR_HEIGHT: f64 = 30.0; const SIDEBAR_ITEM_HEIGHT: f64 = 21.0; -pub fn left_sidebar_view() -> impl View { +pub fn left_sidebar_view() -> impl IntoView { let long_list: im::Vector = (0..100).collect(); let long_list = create_rw_signal(long_list); diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs index 4ef4ab05..ea1c6426 100644 --- a/examples/layout/src/main.rs +++ b/examples/layout/src/main.rs @@ -3,10 +3,9 @@ use floem::{ keyboard::{Key, NamedKey}, kurbo::Size, style::AlignContent, - view::View, - views::{container, h_stack, label, v_stack, Decorators}, - widgets::button, + views::{button, container, h_stack, label, v_stack, Decorators}, window::{new_window, WindowConfig}, + IntoView, View, }; pub mod draggable_sidebar; @@ -15,7 +14,7 @@ pub mod left_sidebar; pub mod right_sidebar; pub mod tab_navigation; -fn list_item(name: String, view_fn: impl Fn() -> V) -> impl View { +fn list_item(name: String, view_fn: impl Fn() -> V) -> impl IntoView { h_stack(( label(move || name.clone()).style(|s| s), container(view_fn()).style(|s| s.width_full().justify_content(AlignContent::End)), @@ -23,7 +22,7 @@ fn list_item(name: String, view_fn: impl Fn() -> V) -> impl V .style(|s| s.width(200)) } -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let view = v_stack(( label(move || String::from("Static layouts")) .style(|s| s.font_size(30.0).margin_bottom(15.0)), diff --git a/examples/layout/src/right_sidebar.rs b/examples/layout/src/right_sidebar.rs index 8fe6a09e..6fb0abf6 100644 --- a/examples/layout/src/right_sidebar.rs +++ b/examples/layout/src/right_sidebar.rs @@ -3,18 +3,18 @@ use floem::{ peniko::Color, reactive::create_rw_signal, style::Position, - view::View, views::{ container, h_stack, label, scroll, v_stack, virtual_stack, Decorators, VirtualDirection, VirtualItemSize, }, + IntoView, View, }; const SIDEBAR_WIDTH: f64 = 140.0; const TOPBAR_HEIGHT: f64 = 30.0; const SIDEBAR_ITEM_HEIGHT: f64 = 21.0; -pub fn right_sidebar_view() -> impl View { +pub fn right_sidebar_view() -> impl IntoView { let long_list: im::Vector = (0..100).collect(); let long_list = create_rw_signal(long_list); diff --git a/examples/layout/src/tab_navigation.rs b/examples/layout/src/tab_navigation.rs index 99a9be54..02864df9 100644 --- a/examples/layout/src/tab_navigation.rs +++ b/examples/layout/src/tab_navigation.rs @@ -4,8 +4,8 @@ use floem::{ peniko::Color, reactive::{create_signal, ReadSignal, WriteSignal}, style::{CursorStyle, Position}, - view::View, views::{container, h_stack, label, scroll, tab, v_stack, Decorators}, + IntoView, View, }; #[derive(Clone, Copy, Eq, Hash, PartialEq)] @@ -30,7 +30,7 @@ fn tab_button( tabs: ReadSignal>, set_active_tab: WriteSignal, active_tab: ReadSignal, -) -> impl View { +) -> impl IntoView { label(move || this_tab) .style(|s| s.justify_center()) .keyboard_navigatable() @@ -62,7 +62,7 @@ fn tab_button( const TABBAR_HEIGHT: f64 = 37.0; const CONTENT_PADDING: f64 = 10.0; -pub fn tab_navigation_view() -> impl View { +pub fn tab_navigation_view() -> impl IntoView { let tabs = vec![Tabs::General, Tabs::Settings, Tabs::Feedback] .into_iter() .collect::>(); diff --git a/examples/responsive/src/main.rs b/examples/responsive/src/main.rs index 2fd4fb36..fa9015c2 100644 --- a/examples/responsive/src/main.rs +++ b/examples/responsive/src/main.rs @@ -4,11 +4,11 @@ use floem::{ responsive::{range, ScreenSize}, style::TextOverflow, unit::UnitExt, - view::View, views::{h_stack, label, stack, text, Decorators}, + IntoView, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let (is_text_overflown, set_is_text_overflown) = create_signal(false); stack({ diff --git a/examples/syntax-editor/src/main.rs b/examples/syntax-editor/src/main.rs index a9ed897b..41f584df 100644 --- a/examples/syntax-editor/src/main.rs +++ b/examples/syntax-editor/src/main.rs @@ -2,6 +2,7 @@ use floem::cosmic_text::{Attrs, AttrsList, Stretch, Style, Weight}; use floem::keyboard::Modifiers; use floem::peniko::Color; use floem::reactive::RwSignal; +use floem::views::button; use floem::views::editor::core::buffer::rope_text::RopeText; use floem::views::editor::id::EditorId; use floem::views::editor::layout::TextLayoutLine; @@ -10,7 +11,6 @@ use floem::views::editor::EditorStyle; use floem::{ cosmic_text::FamilyOwned, keyboard::{Key, NamedKey}, - view::View, views::{ editor::{ core::{editor::EditType, selection::Selection}, @@ -18,8 +18,8 @@ use floem::{ }, stack, text_editor, Decorators, }, - widgets::button, }; +use floem::{IntoView, View}; use lazy_static::lazy_static; use std::borrow::Cow; use std::cell::RefCell; @@ -176,7 +176,7 @@ impl<'a> Styling for SyntaxHighlightingStyle<'a> { } } -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let global_style = SimpleStylingBuilder::default() .wrap(WrapMethod::None) .font_family(vec![ diff --git a/examples/themes/src/main.rs b/examples/themes/src/main.rs index 12df7b0a..106575c5 100644 --- a/examples/themes/src/main.rs +++ b/examples/themes/src/main.rs @@ -5,15 +5,15 @@ use floem::{ reactive::create_signal, style::{Background, BorderColor, Outline, OutlineColor, Style, TextColor, Transition}, style_class, - view::View, views::{label, stack, text, Decorators}, + {IntoView, View}, }; style_class!(pub Button); style_class!(pub Label); style_class!(pub Frame); -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let blue_button = Style::new() .background(Color::rgb8(137, 145, 160)) .color(Color::WHITE) diff --git a/examples/timer/src/main.rs b/examples/timer/src/main.rs index 00469add..e76697e6 100644 --- a/examples/timer/src/main.rs +++ b/examples/timer/src/main.rs @@ -4,16 +4,15 @@ use floem::{ action::exec_after, reactive::{create_effect, create_rw_signal}, unit::UnitExt, - view::View, - views::{container, label, stack, text, v_stack, Decorators}, - widgets::{button, slider}, + views::{button, container, label, slider, stack, text, v_stack, Decorators}, + IntoView, }; fn main() { floem::launch(app_view); } -fn app_view() -> impl View { +fn app_view() -> impl IntoView { // We take maximum duration as 100s for convenience so that // one percent represents one second. let target_duration = create_rw_signal(100.0); diff --git a/examples/virtual_list/src/main.rs b/examples/virtual_list/src/main.rs index 39e00965..1bf872ff 100644 --- a/examples/virtual_list/src/main.rs +++ b/examples/virtual_list/src/main.rs @@ -1,13 +1,13 @@ use floem::{ reactive::create_signal, unit::UnitExt, - view::View, - views::Decorators, - views::{container, label, scroll, VirtualDirection, VirtualItemSize}, - widgets::virtual_list, + views::{ + container, label, scroll, virtual_list, Decorators, VirtualDirection, VirtualItemSize, + }, + IntoView, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let long_list: im::Vector = (0..1000000).collect(); let (long_list, _set_long_list) = create_signal(long_list); diff --git a/examples/widget-gallery/src/buttons.rs b/examples/widget-gallery/src/buttons.rs index f39e0622..e7f8bcd4 100644 --- a/examples/widget-gallery/src/buttons.rs +++ b/examples/widget-gallery/src/buttons.rs @@ -1,14 +1,13 @@ use floem::{ peniko::Color, style::CursorStyle, - view::View, - views::Decorators, - widgets::{self, button, toggle_button}, + views::{button, toggle_button, Decorators, ToggleHandleBehavior}, + IntoView, }; use crate::form::{form, form_item}; -pub fn button_view() -> impl View { +pub fn button_view() -> impl IntoView { form({ ( form_item("Basic Button:".to_string(), 120.0, || { @@ -49,14 +48,14 @@ pub fn button_view() -> impl View { .on_toggle(|_| { println!("Button Toggled"); }) - .toggle_style(|s| s.behavior(widgets::ToggleHandleBehavior::Snap)) + .toggle_style(|s| s.behavior(ToggleHandleBehavior::Snap)) }), form_item("Toggle button - Follow:".to_string(), 120.0, || { toggle_button(|| true) .on_toggle(|_| { println!("Button Toggled"); }) - .toggle_style(|s| s.behavior(widgets::ToggleHandleBehavior::Follow)) + .toggle_style(|s| s.behavior(ToggleHandleBehavior::Follow)) }), ) }) diff --git a/examples/widget-gallery/src/checkbox.rs b/examples/widget-gallery/src/checkbox.rs index 94df0bff..09e2b6d2 100644 --- a/examples/widget-gallery/src/checkbox.rs +++ b/examples/widget-gallery/src/checkbox.rs @@ -1,13 +1,12 @@ use floem::{ reactive::create_signal, - view::View, - views::Decorators, - widgets::{checkbox, labeled_checkbox}, + views::{checkbox, labeled_checkbox, Decorators}, + IntoView, }; use crate::form::{form, form_item}; -pub fn checkbox_view() -> impl View { +pub fn checkbox_view() -> impl IntoView { let width = 160.0; let (is_checked, set_is_checked) = create_signal(true); form({ diff --git a/examples/widget-gallery/src/clipboard.rs b/examples/widget-gallery/src/clipboard.rs index 1a19b89d..faf01d48 100644 --- a/examples/widget-gallery/src/clipboard.rs +++ b/examples/widget-gallery/src/clipboard.rs @@ -1,14 +1,12 @@ use floem::{ reactive::create_rw_signal, - view::View, - views::{h_stack, label, v_stack, Decorators}, - widgets::{button, text_input}, - Clipboard, + views::{button, h_stack, label, text_input, v_stack, Decorators}, + Clipboard, IntoView, }; use crate::form::{form, form_item}; -pub fn clipboard_view() -> impl View { +pub fn clipboard_view() -> impl IntoView { let text1 = create_rw_signal("".to_string()); let text2 = create_rw_signal("-".to_string()); diff --git a/examples/widget-gallery/src/context_menu.rs b/examples/widget-gallery/src/context_menu.rs index e4f57834..578e7ba2 100644 --- a/examples/widget-gallery/src/context_menu.rs +++ b/examples/widget-gallery/src/context_menu.rs @@ -1,10 +1,10 @@ use floem::{ menu::{Menu, MenuItem}, - view::View, views::{label, stack, Decorators}, + IntoView, }; -pub fn menu_view() -> impl View { +pub fn menu_view() -> impl IntoView { stack({ ( label(|| "Click me (Popout menu)") diff --git a/examples/widget-gallery/src/dropdown.rs b/examples/widget-gallery/src/dropdown.rs index ba179629..ac05ef6b 100644 --- a/examples/widget-gallery/src/dropdown.rs +++ b/examples/widget-gallery/src/dropdown.rs @@ -4,9 +4,8 @@ use floem::{ peniko::Color, reactive::create_rw_signal, unit::UnitExt, - view::View, - views::{container, label, stack, svg, Decorators}, - widgets::dropdown::dropdown, + views::{container, dropdown::dropdown, label, stack, svg, Decorators}, + IntoView, }; use crate::form::{self, form_item}; @@ -29,7 +28,7 @@ const CHEVRON_DOWN: &str = r##" "##; -pub fn dropdown_view() -> impl View { +pub fn dropdown_view() -> impl IntoView { let show_dropdown = create_rw_signal(false); let main_drop_view = move |item| { @@ -46,7 +45,7 @@ pub fn dropdown_view() -> impl View { }), )) .style(|s| s.items_center().justify_between().size_full()) - .any() + .into_any() }; form::form({ @@ -59,7 +58,7 @@ pub fn dropdown_view() -> impl View { // iterator to build list in dropdown Values::iter(), // view for each item in the list - |item| label(move || item).any(), + |item| label(move || item).into_any(), ) .show_list(move || show_dropdown.get()) .on_accept(move |_val| show_dropdown.set(false)) diff --git a/examples/widget-gallery/src/form.rs b/examples/widget-gallery/src/form.rs index d3efa98d..91359a2a 100644 --- a/examples/widget-gallery/src/form.rs +++ b/examples/widget-gallery/src/form.rs @@ -1,12 +1,12 @@ use floem::{ cosmic_text::Weight, unit::UnitExt, - view::View, view_tuple::ViewTuple, views::{container, label, stack, Decorators}, + IntoView, }; -pub fn form(children: VT) -> impl View { +pub fn form(children: VT) -> impl IntoView { stack(children).style(|s| { s.flex_col() .items_start() @@ -16,11 +16,11 @@ pub fn form(children: VT) -> impl View { }) } -pub fn form_item( +pub fn form_item( item_label: String, label_width: f32, view_fn: impl Fn() -> V, -) -> impl View { +) -> impl IntoView { container( stack(( container(label(move || item_label.clone()).style(|s| s.font_weight(Weight::BOLD))) diff --git a/examples/widget-gallery/src/images.rs b/examples/widget-gallery/src/images.rs index 30871e18..eca7ad01 100644 --- a/examples/widget-gallery/src/images.rs +++ b/examples/widget-gallery/src/images.rs @@ -1,12 +1,12 @@ use floem::{ unit::UnitExt, - view::View, views::{img, svg, Decorators}, + IntoView, }; use crate::form::{form, form_item}; -pub fn img_view() -> impl View { +pub fn img_view() -> impl IntoView { let ferris_png = include_bytes!("./../assets/ferris.png"); let ferris_svg = include_str!("./../assets/ferris.svg"); let svg_str = r##" diff --git a/examples/widget-gallery/src/inputs.rs b/examples/widget-gallery/src/inputs.rs index eb561fd8..a01362f4 100644 --- a/examples/widget-gallery/src/inputs.rs +++ b/examples/widget-gallery/src/inputs.rs @@ -2,14 +2,13 @@ use floem::{ cosmic_text::{self, Weight}, peniko::Color, reactive::create_rw_signal, - view::View, - views::{Decorators, SelectionCornerRadius}, - widgets::{text_input, PlaceholderTextClass}, + views::{text_input, Decorators, PlaceholderTextClass, SelectionCornerRadius}, + IntoView, }; use crate::form::{form, form_item}; -pub fn text_input_view() -> impl View { +pub fn text_input_view() -> impl IntoView { let text = create_rw_signal("".to_string()); form({ diff --git a/examples/widget-gallery/src/labels.rs b/examples/widget-gallery/src/labels.rs index 32640177..f82c1220 100644 --- a/examples/widget-gallery/src/labels.rs +++ b/examples/widget-gallery/src/labels.rs @@ -1,14 +1,13 @@ use floem::{ cosmic_text::{Style as FontStyle, Weight}, peniko::Color, - view::View, - views::{label, static_label, Decorators}, - widgets::tooltip, + views::{label, static_label, tooltip, Decorators}, + IntoView, }; use crate::form::{form, form_item}; -pub fn label_view() -> impl View { +pub fn label_view() -> impl IntoView { form({ ( form_item("Simple Label:".to_string(), 120.0, || { diff --git a/examples/widget-gallery/src/lists.rs b/examples/widget-gallery/src/lists.rs index 5e962a64..c2b3f1c7 100644 --- a/examples/widget-gallery/src/lists.rs +++ b/examples/widget-gallery/src/lists.rs @@ -3,17 +3,17 @@ use floem::{ peniko::Color, reactive::create_signal, style::JustifyContent, - view::View, views::{ - container, h_stack, h_stack_from_iter, label, scroll, stack, v_stack, v_stack_from_iter, - Decorators, VirtualDirection, VirtualItemSize, VirtualVector, + button, checkbox, container, h_stack, h_stack_from_iter, label, list, scroll, stack, + v_stack, v_stack_from_iter, virtual_list, Decorators, VirtualDirection, VirtualItemSize, + VirtualVector, }, - widgets::{button, checkbox, list, virtual_list}, + IntoView, }; use crate::form::{form, form_item}; -pub fn virt_list_view() -> impl View { +pub fn virt_list_view() -> impl IntoView { v_stack(( h_stack({ ( @@ -34,15 +34,17 @@ pub fn virt_list_view() -> impl View { )) } -fn simple_list() -> impl View { +fn simple_list() -> impl IntoView { scroll( - list((0..100).map(|i| label(move || i.to_string()).style(|s| s.height(24.0)))) - .style(|s| s.width_full()), + list( + (0..100).map(|i| label(move || i.to_string()).style(|s| s.height(24.0).items_center())), + ) + .style(|s| s.width_full()), ) .style(|s| s.width(100.0).height(200.0).border(1.0)) } -fn enhanced_list() -> impl View { +fn enhanced_list() -> impl IntoView { let long_list: im::Vector = (0..100).collect(); let (long_list, set_long_list) = create_signal(long_list); @@ -59,11 +61,15 @@ fn enhanced_list() -> impl View { container({ stack({ ( - checkbox(move || is_checked.get()).on_click_stop(move |_| { - set_is_checked.update(|checked: &mut bool| *checked = !*checked); + checkbox(move || is_checked.get()) + .style(|s| s.margin_left(6)) + .on_click_stop(move |_| { + set_is_checked + .update(|checked: &mut bool| *checked = !*checked); + }), + label(move || item.to_string()).style(|s| { + s.margin_left(6).height(32.0).font_size(22.0).items_center() }), - label(move || item.to_string()) - .style(|s| s.height(32.0).font_size(22.0)), container({ label(move || " X ") .on_click_stop(move |_| { @@ -93,9 +99,12 @@ fn enhanced_list() -> impl View { .style(move |s| s.height_full().width_full().items_center()) }) .style(move |s| { - s.flex_row().height(item_height).apply_if(index != 0, |s| { - s.border_top(1.0).border_color(Color::LIGHT_GRAY) - }) + s.flex_row() + .items_center() + .height(item_height) + .apply_if(index != 0, |s| { + s.border_top(1.0).border_color(Color::LIGHT_GRAY) + }) }) }, ) @@ -104,12 +113,12 @@ fn enhanced_list() -> impl View { .style(move |s| s.width(list_width).height(200.0).border(1.0)) } -fn h_buttons_from_iter() -> impl View { +fn h_buttons_from_iter() -> impl IntoView { let button_iter = (0..3).map(|i| button(move || format!("Button {}", i))); h_stack_from_iter(button_iter) } -fn v_buttons_from_iter() -> impl View { +fn v_buttons_from_iter() -> impl IntoView { let button_iter = (0..3).map(|i| button(move || format!("Button {}", i))); v_stack_from_iter(button_iter) } diff --git a/examples/widget-gallery/src/main.rs b/examples/widget-gallery/src/main.rs index cdc170c4..f742c4bb 100644 --- a/examples/widget-gallery/src/main.rs +++ b/examples/widget-gallery/src/main.rs @@ -13,22 +13,20 @@ pub mod rich_text; pub mod slider; use floem::{ - event::{Event, EventListener}, + event::{Event, EventListener, EventPropagation}, keyboard::{Key, NamedKey}, peniko::Color, reactive::create_signal, style::{Background, CursorStyle, Transition}, unit::UnitExt, - view::View, views::{ - container, h_stack, label, scroll, stack, tab, v_stack, virtual_stack, Decorators, + button, container, h_stack, label, scroll, stack, tab, v_stack, virtual_stack, Decorators, VirtualDirection, VirtualItemSize, }, - widgets::button, - EventPropagation, + IntoView, View, }; -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let tabs: im::Vector<&str> = vec![ "Label", "Button", @@ -153,19 +151,19 @@ fn app_view() -> impl View { move || tabs.get(), |it| *it, |it| match it { - "Label" => labels::label_view().any(), - "Button" => buttons::button_view().any(), - "Checkbox" => checkbox::checkbox_view().any(), - "Radio" => radio_buttons::radio_buttons_view().any(), - "Input" => inputs::text_input_view().any(), - "List" => lists::virt_list_view().any(), - "Menu" => context_menu::menu_view().any(), - "RichText" => rich_text::rich_text_view().any(), - "Image" => images::img_view().any(), - "Clipboard" => clipboard::clipboard_view().any(), - "Slider" => slider::slider_view().any(), - "Dropdown" => dropdown::dropdown_view().any(), - _ => label(|| "Not implemented".to_owned()).any(), + "Label" => labels::label_view().into_any(), + "Button" => buttons::button_view().into_any(), + "Checkbox" => checkbox::checkbox_view().into_any(), + "Radio" => radio_buttons::radio_buttons_view().into_any(), + "Input" => inputs::text_input_view().into_any(), + "List" => lists::virt_list_view().into_any(), + "Menu" => context_menu::menu_view().into_any(), + "RichText" => rich_text::rich_text_view().into_any(), + "Image" => images::img_view().into_any(), + "Clipboard" => clipboard::clipboard_view().into_any(), + "Slider" => slider::slider_view().into_any(), + "Dropdown" => dropdown::dropdown_view().into_any(), + _ => label(|| "Not implemented".to_owned()).into_any(), }, ) .style(|s| s.flex_col().items_start()); diff --git a/examples/widget-gallery/src/radio_buttons.rs b/examples/widget-gallery/src/radio_buttons.rs index a54b318e..6c12723c 100644 --- a/examples/widget-gallery/src/radio_buttons.rs +++ b/examples/widget-gallery/src/radio_buttons.rs @@ -2,9 +2,8 @@ use std::fmt::Display; use floem::{ reactive::create_signal, - view::View, - views::{v_stack, Decorators}, - widgets::{labeled_radio_button, radio_button}, + views::{labeled_radio_button, radio_button, v_stack, Decorators}, + IntoView, }; use crate::form::{form, form_item}; @@ -26,7 +25,7 @@ impl Display for OperatingSystem { } } -pub fn radio_buttons_view() -> impl View { +pub fn radio_buttons_view() -> impl IntoView { let width = 160.0; let (operating_system, set_operating_system) = create_signal(OperatingSystem::Windows); form({ diff --git a/examples/widget-gallery/src/rich_text.rs b/examples/widget-gallery/src/rich_text.rs index ea6c3b26..a5409d74 100644 --- a/examples/widget-gallery/src/rich_text.rs +++ b/examples/widget-gallery/src/rich_text.rs @@ -3,11 +3,11 @@ use std::ops::Range; use floem::{ cosmic_text::{Attrs, AttrsList, Style, TextLayout}, peniko::Color, - view::View, views::{rich_text, scroll}, + IntoView, }; -pub fn rich_text_view() -> impl View { +pub fn rich_text_view() -> impl IntoView { let text = " // floem is a ui lib, homepage https://github.com/lapce/floem fn main() { diff --git a/examples/widget-gallery/src/slider.rs b/examples/widget-gallery/src/slider.rs index ac8b5f4a..fde1ed56 100644 --- a/examples/widget-gallery/src/slider.rs +++ b/examples/widget-gallery/src/slider.rs @@ -1,14 +1,13 @@ use floem::{ reactive::{create_effect, create_rw_signal}, unit::UnitExt, - view::View, - views::{label, stack, text_input, Decorators}, - widgets::slider, + views::{label, slider, stack, text_input, Decorators}, + IntoView, }; use crate::form::{self, form_item}; -pub fn slider_view() -> impl View { +pub fn slider_view() -> impl IntoView { let set_slider = create_rw_signal(50.); let input = create_rw_signal(String::from("50")); create_effect(move |_| { diff --git a/examples/window-scale/src/main.rs b/examples/window-scale/src/main.rs index 933c0a36..168496f6 100644 --- a/examples/window-scale/src/main.rs +++ b/examples/window-scale/src/main.rs @@ -5,13 +5,13 @@ use floem::{ reactive::{create_rw_signal, create_signal}, style_class, unit::UnitExt, - view::View, views::{label, stack, Decorators}, + IntoView, View, }; style_class!(pub Button); -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let (counter, set_counter) = create_signal(0); let window_scale = create_rw_signal(1.0); let view = stack(( diff --git a/examples/window-size/src/main.rs b/examples/window-size/src/main.rs index cf9fa5ec..abbfb671 100644 --- a/examples/window-size/src/main.rs +++ b/examples/window-size/src/main.rs @@ -2,14 +2,12 @@ use floem::{ event::{Event, EventListener}, keyboard::{Key, NamedKey}, kurbo::Size, - view::View, - views::{label, v_stack, Decorators}, - widgets::button, + views::{button, label, v_stack, Decorators}, window::{close_window, new_window, WindowConfig, WindowId}, - Application, + Application, IntoView, View, }; -fn sub_window_view(id: WindowId) -> impl View { +fn sub_window_view(id: WindowId) -> impl IntoView { v_stack(( label(move || String::from("Hello world")).style(|s| s.font_size(30.0)), button(|| "Close this window").on_click_stop(move |_| { @@ -26,7 +24,7 @@ fn sub_window_view(id: WindowId) -> impl View { }) } -fn app_view() -> impl View { +fn app_view() -> impl IntoView { let view = v_stack(( label(move || String::from("Hello world")).style(|s| s.font_size(30.0)), button(|| "Open another window").on_click_stop(|_| { diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index ed500fef..896280fe 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -7,8 +7,8 @@ description = "A native Rust UI library with fine-grained reactivity" license.workspace = true [dependencies] +peniko = { workspace = true } image = { workspace = true } resvg = { workspace = true } -floem-peniko = "0.1.0" -floem-cosmic-text = "0.7.1" +floem-cosmic-text = "0.7.2" diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index f429bd66..2aecff13 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -1,10 +1,10 @@ pub use floem_cosmic_text as cosmic_text; use floem_cosmic_text::TextLayout; -use floem_peniko::{ +use image::DynamicImage; +use peniko::{ kurbo::{Affine, Point, Rect, Shape}, BrushRef, }; -use image::DynamicImage; pub use resvg::tiny_skia; pub use resvg::usvg; diff --git a/src/action.rs b/src/action.rs index 12205f3f..040ae98e 100644 --- a/src/action.rs +++ b/src/action.rs @@ -4,14 +4,14 @@ use std::{ }; use floem_winit::window::ResizeDirection; -use kurbo::{Point, Size, Vec2}; +use peniko::kurbo::{Point, Size, Vec2}; use crate::{ app::{add_app_update_event, AppUpdateEvent}, - id::Id, + id::ViewId, menu::Menu, update::{UpdateMessage, CENTRAL_UPDATE_MESSAGES}, - view::Widget, + view::View, window_handle::{get_current_view, set_current_view}, }; @@ -129,11 +129,11 @@ pub fn set_ime_cursor_area(position: Point, size: Size) { } /// Creates a new overlay on the current window. -pub fn add_overlay( +pub fn add_overlay( position: Point, - view: impl FnOnce(Id) -> V + 'static, -) -> Id { - let id = Id::next(); + view: impl FnOnce(ViewId) -> V + 'static, +) -> ViewId { + let id = ViewId::new(); add_update_message(UpdateMessage::AddOverlay { id, position, @@ -143,6 +143,6 @@ pub fn add_overlay( } /// Removes an overlay from the current window. -pub fn remove_overlay(id: Id) { +pub fn remove_overlay(id: ViewId) { add_update_message(UpdateMessage::RemoveOverlay { id }); } diff --git a/src/animate/anim_val.rs b/src/animate/anim_val.rs index d0cd48aa..15bc69f9 100644 --- a/src/animate/anim_val.rs +++ b/src/animate/anim_val.rs @@ -1,6 +1,6 @@ use std::{any::Any, rc::Rc}; -use floem_peniko::Color; +use peniko::Color; use crate::style::StyleMapValue; diff --git a/src/animate/animation.rs b/src/animate/animation.rs index f298cf07..bffb4128 100644 --- a/src/animate/animation.rs +++ b/src/animate/animation.rs @@ -6,8 +6,8 @@ use super::{ }; use std::{borrow::BorrowMut, collections::HashMap, rc::Rc, time::Duration, time::Instant}; -use floem_peniko::Color; use floem_reactive::create_effect; +use peniko::Color; #[derive(Clone)] pub struct Animation { diff --git a/src/animate/prop.rs b/src/animate/prop.rs index 02054f55..e33dd1dd 100644 --- a/src/animate/prop.rs +++ b/src/animate/prop.rs @@ -1,6 +1,6 @@ use std::{any::Any, rc::Rc}; -use floem_peniko::Color; +use peniko::Color; use crate::{ animate::AnimDirection, diff --git a/src/app.rs b/src/app.rs index e5550d11..dba0095e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -17,7 +17,7 @@ use crate::{ clipboard::Clipboard, inspector::Capture, profiler::Profile, - view::{AnyView, View}, + view::{IntoView, View}, window::WindowConfig, }; @@ -30,7 +30,7 @@ thread_local! { pub(crate) static APP_UPDATE_EVENTS: RefCell> = Default::default(); } -pub fn launch(app_view: impl FnOnce() -> V + 'static) { +pub fn launch(app_view: impl FnOnce() -> V + 'static) { Application::new().window(move |_| app_view(), None).run() } @@ -47,7 +47,7 @@ pub(crate) enum UserEvent { pub(crate) enum AppUpdateEvent { NewWindow { - view_fn: Box AnyView>, + view_fn: Box Box>, config: Option, }, CloseWindow { @@ -120,14 +120,14 @@ impl Application { /// create a new window for the application, if you want multiple windows, /// just chain more window method to the builder - pub fn window( + pub fn window( mut self, app_view: impl FnOnce(WindowId) -> V + 'static, config: Option, ) -> Self { self.handle.as_mut().unwrap().new_window( &self.event_loop, - Box::new(|window_id| app_view(window_id).any()), + Box::new(|window_id| app_view(window_id).into_any()), config, ); self diff --git a/src/app_handle.rs b/src/app_handle.rs index dbca166e..2f203158 100644 --- a/src/app_handle.rs +++ b/src/app_handle.rs @@ -6,7 +6,7 @@ use floem_winit::{ event_loop::{ControlFlow, EventLoopWindowTarget}, window::WindowId, }; -use kurbo::{Point, Size}; +use peniko::kurbo::{Point, Size}; use crate::{ action::{Timer, TimerToken}, @@ -14,7 +14,7 @@ use crate::{ ext_event::EXT_EVENT_HANDLER, inspector::Capture, profiler::{Profile, ProfileEvent}, - view::AnyView, + view::View, window::WindowConfig, window_handle::WindowHandle, }; @@ -245,7 +245,7 @@ impl ApplicationHandle { pub(crate) fn new_window( &mut self, event_loop: &EventLoopWindowTarget, - view_fn: Box AnyView>, + view_fn: Box Box>, config: Option, ) { let mut window_builder = floem_winit::window::WindowBuilder::new(); diff --git a/src/app_state.rs b/src/app_state.rs new file mode 100644 index 00000000..6445156a --- /dev/null +++ b/src/app_state.rs @@ -0,0 +1,333 @@ +use std::collections::{HashMap, HashSet}; + +use floem_winit::window::CursorIcon; +use peniko::kurbo::{Point, Size}; +use taffy::{AvailableSpace, NodeId}; + +use crate::{ + animate::AnimId, + context::{DragState, FrameUpdate, InteractionState}, + event::{Event, EventListener}, + id::ViewId, + inspector::CaptureState, + menu::Menu, + responsive::{GridBreakpoints, ScreenSizeBp}, + style::{CursorStyle, Style, StyleClassRef, StyleSelector}, + view_storage::VIEW_STORAGE, +}; + +/// Encapsulates and owns the global state of the application, +pub struct AppState { + /// keyboard focus + pub(crate) focus: Option, + /// when a view is active, it gets mouse event even when the mouse is + /// not on it + pub(crate) active: Option, + pub(crate) root_view_id: ViewId, + pub(crate) root: Option, + pub(crate) root_size: Size, + pub(crate) scale: f64, + pub(crate) scheduled_updates: Vec, + pub(crate) request_compute_layout: bool, + pub(crate) request_paint: bool, + pub(crate) disabled: HashSet, + pub(crate) keyboard_navigable: HashSet, + pub(crate) draggable: HashSet, + pub(crate) dragging: Option, + pub(crate) drag_start: Option<(ViewId, Point)>, + pub(crate) dragging_over: HashSet, + pub(crate) screen_size_bp: ScreenSizeBp, + pub(crate) grid_bps: GridBreakpoints, + pub(crate) clicking: HashSet, + pub(crate) hovered: HashSet, + /// This keeps track of all views that have an animation, + /// regardless of the status of the animation + pub(crate) cursor: Option, + pub(crate) last_cursor: CursorIcon, + pub(crate) keyboard_navigation: bool, + pub(crate) window_menu: HashMap>, + pub(crate) context_menu: HashMap>, + + /// This is set if we're currently capturing the window for the inspector. + pub(crate) capture: Option, +} + +impl AppState { + pub fn new(root_view_id: ViewId) -> Self { + Self { + root: None, + root_view_id, + focus: None, + active: None, + scale: 1.0, + root_size: Size::ZERO, + screen_size_bp: ScreenSizeBp::Xs, + scheduled_updates: Vec::new(), + request_paint: false, + request_compute_layout: false, + disabled: HashSet::new(), + keyboard_navigable: HashSet::new(), + draggable: HashSet::new(), + dragging: None, + drag_start: None, + dragging_over: HashSet::new(), + clicking: HashSet::new(), + hovered: HashSet::new(), + cursor: None, + last_cursor: CursorIcon::Default, + keyboard_navigation: false, + grid_bps: GridBreakpoints::default(), + window_menu: HashMap::new(), + context_menu: HashMap::new(), + capture: None, + } + } + + /// This removes a view from the app state. + pub fn remove_view(&mut self, id: ViewId) { + let children = id.children(); + for child in children { + self.remove_view(child); + } + let view_state = id.state(); + if let Some(action) = view_state.borrow().cleanup_listener.as_ref() { + action(); + } + let node = view_state.borrow().node; + let taffy = id.taffy(); + let mut taffy = taffy.borrow_mut(); + + let children = taffy.children(node); + if let Ok(children) = children { + for child in children { + let _ = taffy.remove(child); + } + } + let _ = taffy.remove(node); + id.remove(); + self.disabled.remove(&id); + self.keyboard_navigable.remove(&id); + self.draggable.remove(&id); + self.dragging_over.remove(&id); + self.clicking.remove(&id); + self.hovered.remove(&id); + self.clicking.remove(&id); + if self.focus == Some(id) { + self.focus = None; + } + if self.active == Some(id) { + self.active = None; + } + } + + pub(crate) fn can_focus(&self, id: ViewId) -> bool { + self.keyboard_navigable.contains(&id) && !self.is_disabled(&id) && !id.is_hidden_recursive() + } + + pub fn is_hovered(&self, id: &ViewId) -> bool { + self.hovered.contains(id) + } + + pub fn is_disabled(&self, id: &ViewId) -> bool { + self.disabled.contains(id) + } + + pub fn is_focused(&self, id: &ViewId) -> bool { + self.focus.map(|f| &f == id).unwrap_or(false) + } + + pub fn is_active(&self, id: &ViewId) -> bool { + self.active.map(|a| &a == id).unwrap_or(false) + } + + pub fn is_clicking(&self, id: &ViewId) -> bool { + self.clicking.contains(id) + } + + pub fn is_dragging(&self) -> bool { + self.dragging + .as_ref() + .map(|d| d.released_at.is_none()) + .unwrap_or(false) + } + + pub fn set_root_size(&mut self, size: Size) { + self.root_size = size; + self.compute_layout(); + } + + #[allow(clippy::too_many_arguments)] + pub(crate) fn compute_style( + &mut self, + view_id: ViewId, + view_style: Option