diff --git a/examples/dyn-container/src/main.rs b/examples/dyn-container/src/main.rs index 1fc32d26..26947466 100644 --- a/examples/dyn-container/src/main.rs +++ b/examples/dyn-container/src/main.rs @@ -44,8 +44,8 @@ fn app_view() -> impl View { dyn_container( move || view.get(), move |value| match value { - ViewSwitcher::One => Box::new(view_one()), - ViewSwitcher::Two => Box::new(view_two(view)), + ViewSwitcher::One => view_one().any(), + ViewSwitcher::Two => view_two(view).any(), }, ) .style(|s| s.padding(10).border(1)), diff --git a/examples/flight_booker/src/main.rs b/examples/flight_booker/src/main.rs index f573eec1..3fd710ab 100644 --- a/examples/flight_booker/src/main.rs +++ b/examples/flight_booker/src/main.rs @@ -85,11 +85,11 @@ pub fn app_view() -> impl View { let success_message = dyn_container( move || did_booking.get(), move |booked| match (booked, flight_mode.get()) { - (true, FlightMode::OneWay) => Box::new(text(oneway_message(start_text.get()))), + (true, FlightMode::OneWay) => text(oneway_message(start_text.get())).any(), (true, FlightMode::Return) => { - Box::new(text(return_message(start_text.get(), return_text.get()))) + text(return_message(start_text.get(), return_text.get())).any() } - (false, _) => Box::new(empty()), + (false, _) => empty().any(), }, ); diff --git a/src/action.rs b/src/action.rs index de64ba34..9c94a246 100644 --- a/src/action.rs +++ b/src/action.rs @@ -15,7 +15,7 @@ use crate::{ id::Id, menu::Menu, update::{UpdateMessage, CENTRAL_UPDATE_MESSAGES}, - view::View, + view::Widget, window_handle::{get_current_view, set_current_view}, }; @@ -165,7 +165,10 @@ pub fn set_ime_cursor_area(position: Point, size: Size) { } /// Creates a new overlay on the current window. -pub fn add_overlay(position: Point, view: impl FnOnce(Id) -> V + 'static) -> Id { +pub fn add_overlay( + position: Point, + view: impl FnOnce(Id) -> V + 'static, +) -> Id { let id = Id::next(); add_update_message(UpdateMessage::AddOverlay { id, diff --git a/src/app.rs b/src/app.rs index b3ffa6ad..e9aab9a7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,8 +10,13 @@ use once_cell::sync::Lazy; use parking_lot::Mutex; use crate::{ - action::Timer, app_handle::ApplicationHandle, clipboard::Clipboard, inspector::Capture, - profiler::Profile, view::View, window::WindowConfig, + action::Timer, + app_handle::ApplicationHandle, + clipboard::Clipboard, + inspector::Capture, + profiler::Profile, + view::{AnyView, View}, + window::WindowConfig, }; use raw_window_handle::HasRawDisplayHandle; @@ -42,7 +47,7 @@ pub(crate) enum UserEvent { pub(crate) enum AppUpdateEvent { NewWindow { - view_fn: Box Box>, + view_fn: Box AnyView>, config: Option, }, CloseWindow { @@ -121,7 +126,7 @@ impl Application { ) -> Self { self.handle.as_mut().unwrap().new_window( &self.event_loop, - Box::new(|window_id| Box::new(app_view(window_id))), + Box::new(|window_id| app_view(window_id).any()), config, ); self diff --git a/src/app_handle.rs b/src/app_handle.rs index 84b117ec..32e2ca39 100644 --- a/src/app_handle.rs +++ b/src/app_handle.rs @@ -14,7 +14,7 @@ use crate::{ ext_event::EXT_EVENT_HANDLER, inspector::Capture, profiler::{Profile, ProfileEvent}, - view::View, + view::AnyView, window::WindowConfig, window_handle::WindowHandle, }; @@ -239,7 +239,7 @@ impl ApplicationHandle { pub(crate) fn new_window( &mut self, event_loop: &EventLoopWindowTarget, - view_fn: Box Box>, + view_fn: Box AnyView>, config: Option, ) { let mut window_builder = floem_winit::window::WindowBuilder::new(); diff --git a/src/context.rs b/src/context.rs index b6d93b48..2d412194 100644 --- a/src/context.rs +++ b/src/context.rs @@ -26,7 +26,7 @@ use crate::{ ZIndex, }, unit::PxPct, - view::{paint_bg, paint_border, paint_outline, View, ViewData}, + view::{paint_bg, paint_border, paint_outline, ViewData, Widget}, view_data::ChangeFlags, }; @@ -172,12 +172,12 @@ impl AppState { } /// This removes a view from the app state. - pub fn remove_view(&mut self, view: &mut dyn View) { + pub fn remove_view(&mut self, view: &mut dyn Widget) { view.for_each_child_mut(&mut |child| { self.remove_view(child); false }); - let id = view.id(); + let id = view.view_data().id(); let view_state = self.view_state(id); if let Some(action) = view_state.cleanup_listener.as_ref() { action(); @@ -597,11 +597,11 @@ impl<'a> EventCx<'a> { /// Internal method used by Floem. This can be called from parent `View`s to propagate an event to the child `View`. pub fn view_event( &mut self, - view: &mut dyn View, + view: &mut dyn Widget, id_path: Option<&[Id]>, event: Event, ) -> EventPropagation { - if self.should_send(view.id(), &event) { + if self.should_send(view.view_data().id(), &event) { self.unconditional_view_event(view, id_path, event) } else { EventPropagation::Continue @@ -611,11 +611,11 @@ impl<'a> EventCx<'a> { /// Internal method used by Floem. This can be called from parent `View`s to propagate an event to the child `View`. pub(crate) fn unconditional_view_event( &mut self, - view: &mut dyn View, + view: &mut dyn Widget, id_path: Option<&[Id]>, event: Event, ) -> EventPropagation { - let id = view.id(); + let id = view.view_data().id(); if self.app_state.is_hidden(id) { // we don't process events for hidden view return EventPropagation::Continue; @@ -645,7 +645,7 @@ impl<'a> EventCx<'a> { let id = id_path[0]; let id_path = &id_path[1..]; - if id != view.id() { + if id != view.view_data().id() { // This shouldn't happen return EventPropagation::Continue; } @@ -1050,9 +1050,9 @@ impl<'a> StyleCx<'a> { } /// Internal method used by Floem to compute the styles for the view. - pub fn style_view(&mut self, view: &mut dyn View) { + pub fn style_view(&mut self, view: &mut dyn Widget) { self.save(); - let id = view.id(); + let id = view.view_data().id(); let view_state = self.app_state_mut().view_state(id); if !view_state.requested_changes.contains(ChangeFlags::STYLE) { return; @@ -1074,7 +1074,7 @@ impl<'a> StyleCx<'a> { if view_state.request_style_recursive { view_state.request_style_recursive = false; view.for_each_child(&mut |child| { - let state = self.app_state_mut().view_state(child.id()); + let state = self.app_state_mut().view_state(child.view_data().id()); state.request_style_recursive = true; state.requested_changes.insert(ChangeFlags::STYLE); false @@ -1255,8 +1255,8 @@ impl<'a> ComputeLayoutCx<'a> { /// - invoking any attached context::ResizeListeners /// /// Returns the bounding rect that encompasses this view and its children - pub fn compute_view_layout(&mut self, view: &mut dyn View) -> Option { - let id = view.id(); + pub fn compute_view_layout(&mut self, view: &mut dyn Widget) -> Option { + let id = view.view_data().id(); if self.app_state().is_hidden(id) { self.app_state_mut().view_state(id).layout_rect = Rect::ZERO; return None; @@ -1381,7 +1381,7 @@ impl<'a> LayoutCx<'a> { } /// Internal method used by Floem to invoke the user-defined `View::layout` method. - pub fn layout_view(&mut self, view: &mut dyn View) -> Node { + pub fn layout_view(&mut self, view: &mut dyn Widget) -> Node { view.layout(self) } } @@ -1427,8 +1427,8 @@ impl<'a> PaintCx<'a> { /// - managing hidden status /// - clipping /// - painting computed styles like background color, border, font-styles, and z-index and handling painting requirements of drag and drop - pub fn paint_view(&mut self, view: &mut dyn View) { - let id = view.id(); + pub fn paint_view(&mut self, view: &mut dyn Widget) { + let id = view.view_data().id(); if self.app_state.is_hidden(id) { return; } @@ -1674,10 +1674,10 @@ impl<'a> UpdateCx<'a> { /// Used internally by Floem to send an update to the correct view based on the `Id` path. /// It will invoke only once `update` when the correct view is located. - pub fn update_view(&mut self, view: &mut dyn View, id_path: &[Id], state: Box) { + pub fn update_view(&mut self, view: &mut dyn Widget, id_path: &[Id], state: Box) { let id = id_path[0]; let id_path = &id_path[1..]; - if id == view.id() { + if id == view.view_data().id() { if id_path.is_empty() { view.update(self, state); } else if let Some(child) = view.child_mut(id_path[0]) { diff --git a/src/inspector.rs b/src/inspector.rs index db96df72..7ab36a19 100644 --- a/src/inspector.rs +++ b/src/inspector.rs @@ -4,7 +4,7 @@ use crate::event::{Event, EventListener}; use crate::id::Id; use crate::profiler::profiler; use crate::style::{Style, StyleMapValue}; -use crate::view::{view_children, View}; +use crate::view::{view_children, AnyView, View, Widget}; use crate::view_data::ChangeFlags; use crate::views::{ container, dyn_container, empty, h_stack, img_dynamic, scroll, stack, static_label, tab, text, @@ -42,8 +42,8 @@ pub struct CapturedView { } impl CapturedView { - pub fn capture(view: &dyn View, app_state: &mut AppState, clip: Rect) -> Self { - let id = view.id(); + pub fn capture(view: &dyn Widget, app_state: &mut AppState, clip: Rect) -> Self { + let id = view.view_data().id(); let layout = app_state.get_layout_rect(id); let taffy = app_state.get_layout(id).unwrap(); let computed_style = app_state.get_computed_style(id).clone(); @@ -142,28 +142,32 @@ fn captured_view_name(view: &CapturedView) -> impl View { .font_size(12.0) .color(Color::BLACK.with_alpha_factor(0.6)) }); - let tab: Box = if view.focused { - Box::new(text("Focus").style(|s| { - s.margin_right(5.0) - .background(Color::rgb8(63, 81, 101).with_alpha_factor(0.6)) - .border_radius(5.0) - .padding(1.0) - .font_size(10.0) - .color(Color::WHITE.with_alpha_factor(0.8)) - })) + let tab = if view.focused { + text("Focus") + .style(|s| { + s.margin_right(5.0) + .background(Color::rgb8(63, 81, 101).with_alpha_factor(0.6)) + .border_radius(5.0) + .padding(1.0) + .font_size(10.0) + .color(Color::WHITE.with_alpha_factor(0.8)) + }) + .any() } else if view.keyboard_navigable { - Box::new(text("Tab").style(|s| { - s.margin_right(5.0) - .background(Color::rgb8(204, 217, 221).with_alpha_factor(0.4)) - .border(1.0) - .border_radius(5.0) - .border_color(Color::BLACK.with_alpha_factor(0.07)) - .padding(1.0) - .font_size(10.0) - .color(Color::BLACK.with_alpha_factor(0.4)) - })) + text("Tab") + .style(|s| { + s.margin_right(5.0) + .background(Color::rgb8(204, 217, 221).with_alpha_factor(0.4)) + .border(1.0) + .border_radius(5.0) + .border_color(Color::BLACK.with_alpha_factor(0.07)) + .padding(1.0) + .font_size(10.0) + .color(Color::BLACK.with_alpha_factor(0.4)) + }) + .any() } else { - Box::new(empty()) + empty().any() }; h_stack((id, tab, name)).style(|s| s.items_center()) } @@ -174,7 +178,7 @@ fn captured_view_no_children( view: &CapturedView, depth: usize, capture_view: &CaptureView, -) -> Box { +) -> AnyView { let offset = depth as f64 * 14.0; let name = captured_view_name(view); let name_id = name.id(); @@ -183,33 +187,32 @@ fn captured_view_no_children( let selected = capture_view.selected; let highlighted = capture_view.highlighted; - let row = Box::new( - container(name) - .style(move |s| { - s.padding_left(20.0 + offset) - .hover(move |s| { - s.background(Color::rgba8(228, 237, 216, 160)) - .apply_if(selected.get() == Some(id), |s| { - s.background(Color::rgb8(186, 180, 216)) - }) - }) - .height(height) - .apply_if(highlighted.get() == Some(id), |s| { - s.background(Color::rgba8(228, 237, 216, 160)) - }) - .apply_if(selected.get() == Some(id), |s| { - if highlighted.get() == Some(id) { + let row = container(name) + .style(move |s| { + s.padding_left(20.0 + offset) + .hover(move |s| { + s.background(Color::rgba8(228, 237, 216, 160)) + .apply_if(selected.get() == Some(id), |s| { s.background(Color::rgb8(186, 180, 216)) - } else { - s.background(Color::rgb8(213, 208, 216)) - } - }) - }) - .on_click_stop(move |_| selected.set(Some(id))) - .on_event_cont(EventListener::PointerEnter, move |_| { - highlighted.set(Some(id)) - }), - ); + }) + }) + .height(height) + .apply_if(highlighted.get() == Some(id), |s| { + s.background(Color::rgba8(228, 237, 216, 160)) + }) + .apply_if(selected.get() == Some(id), |s| { + if highlighted.get() == Some(id) { + s.background(Color::rgb8(186, 180, 216)) + } else { + s.background(Color::rgb8(213, 208, 216)) + } + }) + }) + .on_click_stop(move |_| selected.set(Some(id))) + .on_event_cont(EventListener::PointerEnter, move |_| { + highlighted.set(Some(id)) + }) + .any(); let row_id = row.id(); let scroll_to = capture_view.scroll_to; @@ -233,8 +236,8 @@ fn captured_view_with_children( view: &Rc, depth: usize, capture_view: &CaptureView, - children: Vec>, -) -> Box { + children: Vec, +) -> AnyView { let offset = depth as f64 * 14.0; let name = captured_view_name(view); let height = 20.0; @@ -335,14 +338,10 @@ fn captured_view_with_children( let list = v_stack((line, list)); - Box::new(v_stack((row, list))) + v_stack((row, list)).any() } -fn captured_view( - view: &Rc, - depth: usize, - capture_view: &CaptureView, -) -> Box { +fn captured_view(view: &Rc, depth: usize, capture_view: &CaptureView) -> AnyView { if view.children.is_empty() { captured_view_no_children(view, depth, capture_view) } else { @@ -423,7 +422,7 @@ fn stats(capture: &Capture) -> impl View { )) } -fn selected_view(capture: &Rc, selected: RwSignal>) -> impl View { +fn selected_view(capture: &Rc, selected: RwSignal>) -> AnyView { let capture = capture.clone(); dyn_container( move || selected.get(), @@ -545,10 +544,10 @@ fn selected_view(capture: &Rc, selected: RwSignal>) -> impl let style_list = v_stack_from_iter(style_list.into_iter().map(|((prop, name), value)| { let name = name.strip_prefix("floem::style::").unwrap_or(&name); - let name: Box = if direct.contains(&prop) { - Box::new(text(name)) + let name = if direct.contains(&prop) { + text(name).any() } else { - Box::new(stack(( + stack(( text("Inherited").style(|s| { s.margin_right(5.0) .background(Color::WHITE_SMOKE.with_alpha_factor(0.6)) @@ -560,16 +559,16 @@ fn selected_view(capture: &Rc, selected: RwSignal>) -> impl .color(Color::BLACK.with_alpha_factor(0.4)) }), text(name), - ))) + )) + .any() }; - let mut v: Box = match value { + let mut v = match value { StyleMapValue::Val(v) => { let v = &*v; - (prop.info.debug_view)(v).unwrap_or_else(|| { - Box::new(static_label((prop.info.debug_any)(v))) - }) + (prop.info.debug_view)(v) + .unwrap_or_else(|| static_label((prop.info.debug_any)(v)).any()) } - StyleMapValue::Unset => Box::new(text("Unset")), + StyleMapValue::Unset => text("Unset").any(), }; if let Some(transition) = style.transitions.get(&prop).cloned() { let transition = stack(( @@ -587,7 +586,7 @@ fn selected_view(capture: &Rc, selected: RwSignal>) -> impl static_label(format!("{transition:?}")), )) .style(|s| s.items_center()); - v = Box::new(v_stack((v, transition))); + v = v_stack((v, transition)).any(); } stack(( stack((name.style(|s| { @@ -607,30 +606,30 @@ fn selected_view(capture: &Rc, selected: RwSignal>) -> impl })) .style(|s| s.width_full()); - Box::new( - v_stack(( - name, - id, - count, - x, - y, - w, - h, - tx, - ty, - tw, - th, - clear, - style_header, - style_list, - )) - .style(|s| s.width_full()), - ) + v_stack(( + name, + id, + count, + x, + y, + w, + h, + tx, + ty, + tw, + th, + clear, + style_header, + style_list, + )) + .style(|s| s.width_full()) + .any() } else { - Box::new(text("No selection").style(|s| s.padding(5.0))) + text("No selection").style(|s| s.padding(5.0)).any() } }, ) + .any() } #[derive(Clone, Copy)] @@ -790,10 +789,10 @@ fn capture_view(capture: &Rc) -> impl View { .on_click_stop(move |_| capture_view.selected.set(None)) .scroll_to_view(move || capture_view.scroll_to.get()); - let tree: Box = if capture.root.warnings() { - Box::new(v_stack((header("Warnings"), header("View Tree"), tree))) + let tree = if capture.root.warnings() { + v_stack((header("Warnings"), header("View Tree"), tree)).any() } else { - Box::new(v_stack((header("View Tree"), tree))) + v_stack((header("View Tree"), tree)).any() }; let tree = tree.style(|s| s.height_full().min_width(0).flex_basis(0).flex_grow(1.0)); @@ -808,10 +807,10 @@ fn capture_view(capture: &Rc) -> impl View { } fn inspector_view(capture: &Option>) -> impl View { - let view: Box = if let Some(capture) = capture { - Box::new(capture_view(capture)) + let view = if let Some(capture) = capture { + capture_view(capture).any() } else { - Box::new(text("No capture")) + text("No capture").any() }; stack((view,)) @@ -835,7 +834,7 @@ fn inspector_view(capture: &Option>) -> impl View { } thread_local! { - pub(crate) static RUNNING: Cell = Cell::new(false); + pub(crate) static RUNNING: Cell = const { Cell::new(false) }; pub(crate) static CAPTURE: RwSignal>> = { Scope::new().create_rw_signal(None) }; @@ -876,18 +875,15 @@ pub fn capture(window_id: WindowId) { move || selected.get(), move || [0, 1].into_iter(), |it| *it, - move |it| -> Box { - match it { - 0 => Box::new( - dyn_container( - move || capture.get(), - |capture| Box::new(inspector_view(&capture)), - ) - .style(|s| s.width_full().height_full()), - ), - 1 => Box::new(profiler(window_id)), - _ => panic!(), - } + move |it| match it { + 0 => dyn_container( + move || capture.get(), + |capture| inspector_view(&capture).any(), + ) + .style(|s| s.width_full().height_full()) + .any(), + 1 => profiler(window_id).any(), + _ => panic!(), }, ) .style(|s| s.flex_basis(0.0).min_height(0.0).flex_grow(1.0)); diff --git a/src/nav.rs b/src/nav.rs index b6d2aa1e..5142fbf7 100644 --- a/src/nav.rs +++ b/src/nav.rs @@ -4,10 +4,10 @@ use kurbo::{Point, Rect}; use crate::{ context::AppState, id::Id, - view::{view_tab_navigation, View}, + view::{view_tab_navigation, Widget}, }; -pub(crate) fn view_arrow_navigation(key: NamedKey, app_state: &mut AppState, view: &dyn View) { +pub(crate) fn view_arrow_navigation(key: NamedKey, app_state: &mut AppState, view: &dyn Widget) { let focused = match app_state.focus { Some(id) => id, None => { diff --git a/src/profiler.rs b/src/profiler.rs index c76f44c2..bd557030 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -130,18 +130,19 @@ fn profile_view(profile: &Rc) -> impl View { let event_tooltip = dyn_container( move || hovered_event.get(), - move |event: Option| -> Box { + move |event: Option| { if let Some(event) = event { let len = event .end .saturating_duration_since(event.start) .as_secs_f64(); - Box::new(v_stack(( + v_stack(( info("Name", event.name.to_string()), info("Time", format!("{:.4} ms", len * 1000.0)), - ))) + )) + .any() } else { - Box::new(text("No hovered event").style(|s| s.padding(5.0))) + text("No hovered event").style(|s| s.padding(5.0)).any() } }, ) @@ -204,23 +205,22 @@ fn profile_view(profile: &Rc) -> impl View { hovered_event.set(Some(event_.clone())) }) }); - Box::new( - scroll( - v_stack_from_iter(list) - .style(move |s| s.min_width_pct(zoom.get() * 100.0).height_full()), - ) - .style(|s| s.height_full().min_width(0).flex_basis(0).flex_grow(1.0)) - .on_event(EventListener::PointerWheel, move |e| { - if let Event::PointerWheel(e) = e { - zoom.set(zoom.get() * (1.0 - e.delta.y / 400.0)); - EventPropagation::Stop - } else { - EventPropagation::Continue - } - }), + scroll( + v_stack_from_iter(list) + .style(move |s| s.min_width_pct(zoom.get() * 100.0).height_full()), ) + .style(|s| s.height_full().min_width(0).flex_basis(0).flex_grow(1.0)) + .on_event(EventListener::PointerWheel, move |e| { + if let Event::PointerWheel(e) = e { + zoom.set(zoom.get() * (1.0 - e.delta.y / 400.0)); + EventPropagation::Stop + } else { + EventPropagation::Continue + } + }) + .any() } else { - Box::new(text("No selected frame").style(|s| s.padding(5.0))) + text("No selected frame").style(|s| s.padding(5.0)).any() } }, ) @@ -282,9 +282,9 @@ pub fn profiler(window_id: WindowId) -> impl View { move || profile.get(), |profile| { if let Some(profile) = profile { - Box::new(profile_view(&profile)) + profile_view(&profile).any() } else { - Box::new(text("No profile").style(|s| s.padding(5.0))) + text("No profile").style(|s| s.padding(5.0)).any() } }, ) diff --git a/src/style.rs b/src/style.rs index 74689228..b485b7d4 100644 --- a/src/style.rs +++ b/src/style.rs @@ -53,11 +53,11 @@ use taffy::{ use crate::context::InteractionState; use crate::responsive::{ScreenSize, ScreenSizeBp}; use crate::unit::{Px, PxPct, PxPctAuto, UnitExt}; -use crate::view::View; +use crate::view::{AnyView, View}; use crate::views::{empty, stack, text, Decorators}; pub trait StylePropValue: Clone + PartialEq + Debug { - fn debug_view(&self) -> Option> { + fn debug_view(&self) -> Option { None } @@ -96,7 +96,7 @@ impl StylePropValue for LineHeightValue {} impl StylePropValue for Size {} impl StylePropValue for Option { - fn debug_view(&self) -> Option> { + fn debug_view(&self) -> Option { self.as_ref().and_then(|v| v.debug_view()) } @@ -109,7 +109,7 @@ impl StylePropValue for Option { } } impl StylePropValue for Vec { - fn debug_view(&self) -> Option> { + fn debug_view(&self) -> Option { None } @@ -118,34 +118,34 @@ impl StylePropValue for Vec { } } impl StylePropValue for Px { - fn debug_view(&self) -> Option> { - Some(Box::new(text(format!("{} px", self.0)))) + fn debug_view(&self) -> Option { + Some(text(format!("{} px", self.0)).any()) } fn interpolate(&self, other: &Self, value: f64) -> Option { self.0.interpolate(&other.0, value).map(Px) } } impl StylePropValue for PxPctAuto { - fn debug_view(&self) -> Option> { + fn debug_view(&self) -> Option { let label = match self { Self::Px(v) => format!("{} px", v), Self::Pct(v) => format!("{}%", v), Self::Auto => "auto".to_string(), }; - Some(Box::new(text(label))) + Some(text(label).any()) } } impl StylePropValue for PxPct { - fn debug_view(&self) -> Option> { + fn debug_view(&self) -> Option { let label = match self { Self::Px(v) => format!("{} px", v), Self::Pct(v) => format!("{}%", v), }; - Some(Box::new(text(label))) + Some(text(label).any()) } } impl StylePropValue for Color { - fn debug_view(&self) -> Option> { + fn debug_view(&self) -> Option { let color = *self; let color = empty().style(move |s| { s.background(color) @@ -161,9 +161,11 @@ impl StylePropValue for Color { .border_radius(5.0) .margin_left(6.0) }); - Some(Box::new( - stack((text(format!("{self:?}")), color)).style(|s| s.items_center()), - )) + Some( + stack((text(format!("{self:?}")), color)) + .style(|s| s.items_center()) + .any(), + ) } fn interpolate(&self, other: &Self, value: f64) -> Option { @@ -247,7 +249,7 @@ pub struct StylePropInfo { pub(crate) inherited: bool, pub(crate) default_as_any: fn() -> Rc, pub(crate) debug_any: fn(val: &dyn Any) -> String, - pub(crate) debug_view: fn(val: &dyn Any) -> Option>, + pub(crate) debug_view: fn(val: &dyn Any) -> Option, } impl StylePropInfo { diff --git a/src/update.rs b/src/update.rs index 4a9c1477..1caf4532 100644 --- a/src/update.rs +++ b/src/update.rs @@ -10,7 +10,7 @@ use crate::{ id::Id, menu::Menu, style::{Style, StyleClassRef, StyleSelector}, - view::View, + view::Widget, view_data::{ChangeFlags, StackOffset}, }; @@ -116,7 +116,7 @@ pub(crate) enum UpdateMessage { AddOverlay { id: Id, position: Point, - view: Box Box>, + view: Box Box>, }, RemoveOverlay { id: Id, diff --git a/src/view.rs b/src/view.rs index 2fd93105..f5fb4c9e 100644 --- a/src/view.rs +++ b/src/view.rs @@ -99,30 +99,92 @@ use crate::{ pub use crate::view_data::ViewData; -pub trait View { +pub type AnyWidget = Box; + +pub trait View: Sized { + fn view_data(&self) -> &ViewData; + fn view_data_mut(&mut self) -> &mut ViewData; + + fn id(&self) -> Id { + self.view_data().id() + } + + /// This method builds the widget described by the view. Implementations may not assume that an + /// implicit scope is available. It may differ from the scope the view was created in or may not + /// be available at all. + fn build(self) -> AnyWidget; + + /// Converts this view into the `AnyView` type. + fn any(self) -> AnyView + where + Self: 'static, + { + AnyView { + view: Box::new(self), + } + } +} + +/// This is a type which can hold any view. +pub struct AnyView { + view: Box, +} + +impl View for AnyView { + fn view_data(&self) -> &ViewData { + self.view.view_data() + } + + fn view_data_mut(&mut self) -> &mut ViewData { + self.view.view_data_mut() + } + + fn build(self) -> AnyWidget { + self.view.build() + } +} + +trait DynView { + fn view_data(&self) -> &ViewData; + fn view_data_mut(&mut self) -> &mut ViewData; + fn build(self: Box) -> Box; +} + +impl DynView for V { + fn view_data(&self) -> &ViewData { + self.view_data() + } + + fn view_data_mut(&mut self) -> &mut ViewData { + self.view_data_mut() + } + + fn build(self: Box) -> Box { + View::build(*self) + } +} + +pub trait Widget { fn view_data(&self) -> &ViewData; fn view_data_mut(&mut self) -> &mut ViewData; /// This method walks over children and must be implemented if the view has any children. /// It should return children back to front and should stop if `_for_each` returns `true`. - fn for_each_child<'a>(&'a self, _for_each: &mut dyn FnMut(&'a dyn View) -> bool) {} + fn for_each_child<'a>(&'a self, _for_each: &mut dyn FnMut(&'a dyn Widget) -> bool) {} /// This method walks over children and must be implemented if the view has any children. /// It should return children back to front and should stop if `_for_each` returns `true`. - fn for_each_child_mut<'a>(&'a mut self, _for_each: &mut dyn FnMut(&'a mut dyn View) -> bool) {} + fn for_each_child_mut<'a>(&'a mut self, _for_each: &mut dyn FnMut(&'a mut dyn Widget) -> bool) { + } /// This method walks over children and must be implemented if the view has any children. /// It should return children front to back and should stop if `_for_each` returns `true`. fn for_each_child_rev_mut<'a>( &'a mut self, - _for_each: &mut dyn FnMut(&'a mut dyn View) -> bool, + _for_each: &mut dyn FnMut(&'a mut dyn Widget) -> bool, ) { } - fn id(&self) -> Id { - self.view_data().id() - } - fn view_style(&self) -> Option