From af7618c8557d14c4b7e4d64d4394b7d406c74cd9 Mon Sep 17 00:00:00 2001 From: Zoxc Date: Sun, 5 Nov 2023 21:50:13 +0100 Subject: [PATCH] Move pass `View` methods to context types and `AppState` (#145) * Remove `update_main` * Remove `layout_main` * Remove `cleanup` * Remove `style_main` * Remove `compute_layout_main` * Remove `event_main` * Remove `paint_main` --- src/context.rs | 640 ++++++++++++++++++++++++++- src/view.rs | 633 +------------------------- src/views/clip.rs | 12 +- src/views/container.rs | 12 +- src/views/container_box.rs | 12 +- src/views/drag_resize_window_area.rs | 12 +- src/views/drag_window_area.rs | 20 +- src/views/dyn_container.rs | 12 +- src/views/list.rs | 11 +- src/views/scroll.rs | 12 +- src/views/stack.rs | 18 +- src/views/static_list.rs | 11 +- src/views/tab.rs | 12 +- src/views/virtual_list.rs | 9 +- src/window_handle.rs | 42 +- 15 files changed, 735 insertions(+), 733 deletions(-) diff --git a/src/context.rs b/src/context.rs index 3d8f5ab3..8e8dad32 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,5 @@ use std::{ + any::Any, collections::{HashMap, HashSet}, ops::{Deref, DerefMut}, rc::Rc, @@ -18,6 +19,7 @@ use taffy::{ use winit::window::CursorIcon; use crate::{ + action::{exec_after, show_context_menu}, animate::{AnimId, AnimPropKind, Animation}, event::{Event, EventListener}, id::Id, @@ -29,9 +31,10 @@ use crate::{ style::{ Background, BorderBottom, BorderColor, BorderLeft, BorderRadius, BorderRight, BorderTop, BuiltinStyle, CursorStyle, DisplayProp, LayoutProps, Style, StyleClassRef, StyleProp, - StyleSelector, StyleSelectors, + StyleSelector, StyleSelectors, ZIndex, }, unit::PxPct, + view::{paint_bg, paint_border, paint_outline, ChangeFlags, View}, }; pub type EventCallback = dyn Fn(&Event) -> bool; @@ -279,6 +282,42 @@ impl AppState { .or_insert_with(|| ViewState::new(&mut self.taffy)) } + /// This removes a view from the app state. + pub fn remove_view(&mut self, view: &mut dyn View) { + for child in view.children_mut() { + self.remove_view(child); + } + let id = view.id(); + let view_state = self.view_state(id); + if let Some(action) = view_state.cleanup_listener.as_ref() { + action(); + } + let node = view_state.node; + if let Ok(children) = self.taffy.children(node) { + for child in children { + let _ = self.taffy.remove(child); + } + } + let _ = self.taffy.remove(node); + id.remove_id_path(); + self.view_states.remove(&id); + 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.animated.remove(&id); + self.transitioning.remove(&id); + self.clicking.remove(&id); + if self.focus == Some(id) { + self.focus = None; + } + if self.active == Some(id) { + self.active = None; + } + } + pub fn ids_with_anim_in_progress(&mut self) -> Vec { self.animated .clone() @@ -641,6 +680,324 @@ impl<'a> EventCx<'a> { self.app_state.get_layout(id) } + /// 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, + id_path: Option<&[Id]>, + event: Event, + ) -> bool { + if self.should_send(view.id(), &event) { + self.unconditional_view_event(view, id_path, event) + } else { + false + } + } + + /// 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, + id_path: Option<&[Id]>, + event: Event, + ) -> bool { + let id = view.id(); + if self.app_state.is_hidden(id) { + // we don't process events for hidden view + return false; + } + if self.app_state.is_disabled(&id) && !event.allow_disabled() { + // if the view is disabled and the event is not processed + // for disabled views + return false; + } + + // offset the event positions if the event has positions + // e.g. pointer events, so that the position is relative + // to the view, taking into account of the layout location + // of the view and the viewport of the view if it's in a scroll. + let event = self.offset_event(id, event); + + // if there's id_path, it's an event only for a view. + if let Some(id_path) = id_path { + if id_path.is_empty() { + // this happens when the parent is the destination, + // but the parent just passed the event on, + // so it's not really for this view and we stop + // the event propagation. + return false; + } + + let id = id_path[0]; + let id_path = &id_path[1..]; + + if id != view.id() { + // This shouldn't happen + return false; + } + + // we're the parent of the event destination, so pass it on to the child + if !id_path.is_empty() { + if let Some(child) = view.child_mut(id_path[0]) { + return self.unconditional_view_event(child, Some(id_path), event.clone()); + } else { + // we don't have the child, stop the event propagation + return false; + } + } + } + + // if the event was dispatched to an id_path, the event is supposed to be only + // handled by this view only, so we pass an empty id_path + // and the event propagation would be stopped at this view + if view.event( + self, + if id_path.is_some() { Some(&[]) } else { None }, + event.clone(), + ) { + return true; + } + + match &event { + Event::PointerDown(event) => { + self.app_state.clicking.insert(id); + if event.button.is_primary() { + let rect = self.get_size(id).unwrap_or_default().to_rect(); + let now_focused = rect.contains(event.pos); + + if now_focused { + if self.app_state.keyboard_navigable.contains(&id) { + // if the view can be focused, we update the focus + self.app_state.update_focus(id, false); + } + if event.count == 2 + && self.has_event_listener(id, EventListener::DoubleClick) + { + let view_state = self.app_state.view_state(id); + view_state.last_pointer_down = Some(event.clone()); + } + if self.has_event_listener(id, EventListener::Click) { + let view_state = self.app_state.view_state(id); + view_state.last_pointer_down = Some(event.clone()); + } + + let bottom_left = { + let layout = self.app_state.view_state(id).layout_rect; + Point::new(layout.x0, layout.y1) + }; + if let Some(menu) = &self.app_state.view_state(id).popout_menu { + show_context_menu(menu(), Some(bottom_left)) + } + if self.app_state.draggable.contains(&id) + && self.app_state.drag_start.is_none() + { + self.app_state.drag_start = Some((id, event.pos)); + } + } + } else if event.button.is_secondary() { + let rect = self.get_size(id).unwrap_or_default().to_rect(); + let now_focused = rect.contains(event.pos); + + if now_focused { + if self.app_state.keyboard_navigable.contains(&id) { + // if the view can be focused, we update the focus + self.app_state.update_focus(id, false); + } + if self.has_event_listener(id, EventListener::SecondaryClick) { + let view_state = self.app_state.view_state(id); + view_state.last_pointer_down = Some(event.clone()); + } + } + } + } + Event::PointerMove(pointer_event) => { + let rect = self.get_size(id).unwrap_or_default().to_rect(); + if rect.contains(pointer_event.pos) { + if self.app_state.is_dragging() { + self.app_state.dragging_over.insert(id); + if let Some(action) = self.get_event_listener(id, &EventListener::DragOver) + { + (*action)(&event); + } + } else { + self.app_state.hovered.insert(id); + let style = self.app_state.get_builtin_style(id); + if let Some(cursor) = style.cursor() { + if self.app_state.cursor.is_none() { + self.app_state.cursor = Some(cursor); + } + } + } + } + if self.app_state.draggable.contains(&id) { + if let Some((_, drag_start)) = self + .app_state + .drag_start + .as_ref() + .filter(|(drag_id, _)| drag_id == &id) + { + let vec2 = pointer_event.pos - *drag_start; + + if let Some(dragging) = self + .app_state + .dragging + .as_mut() + .filter(|d| d.id == id && d.released_at.is_none()) + { + // update the dragging offset if the view is dragging and not released + dragging.offset = vec2; + id.request_paint(); + } else if vec2.x.abs() + vec2.y.abs() > 1.0 { + // start dragging when moved 1 px + self.app_state.active = None; + self.update_active(id); + self.app_state.dragging = Some(DragState { + id, + offset: vec2, + released_at: None, + }); + id.request_paint(); + if let Some(action) = + self.get_event_listener(id, &EventListener::DragStart) + { + (*action)(&event); + } + } + } + } + if let Some(action) = self.get_event_listener(id, &EventListener::PointerMove) { + if (*action)(&event) { + return true; + } + } + } + Event::PointerUp(pointer_event) => { + if pointer_event.button.is_primary() { + let rect = self.get_size(id).unwrap_or_default().to_rect(); + let on_view = rect.contains(pointer_event.pos); + + if id_path.is_none() { + if on_view { + if let Some(dragging) = self.app_state.dragging.as_mut() { + let dragging_id = dragging.id; + if let Some(action) = + self.get_event_listener(id, &EventListener::Drop) + { + if (*action)(&event) { + // if the drop is processed, we set dragging to none so that the animation + // for the dragged view back to its original position isn't played. + self.app_state.dragging = None; + id.request_paint(); + if let Some(action) = self.get_event_listener( + dragging_id, + &EventListener::DragEnd, + ) { + (*action)(&event); + } + } + } + } + } + } else if let Some(dragging) = + self.app_state.dragging.as_mut().filter(|d| d.id == id) + { + let dragging_id = dragging.id; + dragging.released_at = Some(std::time::Instant::now()); + id.request_paint(); + if let Some(action) = + self.get_event_listener(dragging_id, &EventListener::DragEnd) + { + (*action)(&event); + } + } + + let last_pointer_down = self.app_state.view_state(id).last_pointer_down.take(); + if let Some(action) = self.get_event_listener(id, &EventListener::DoubleClick) { + if on_view + && self.app_state.is_clicking(&id) + && last_pointer_down + .as_ref() + .map(|e| e.count == 2) + .unwrap_or(false) + && (*action)(&event) + { + return true; + } + } + if let Some(action) = self.get_event_listener(id, &EventListener::Click) { + if on_view + && self.app_state.is_clicking(&id) + && last_pointer_down.is_some() + && (*action)(&event) + { + return true; + } + } + + if let Some(action) = self.get_event_listener(id, &EventListener::PointerUp) { + if (*action)(&event) { + return true; + } + } + } else if pointer_event.button.is_secondary() { + let rect = self.get_size(id).unwrap_or_default().to_rect(); + let on_view = rect.contains(pointer_event.pos); + + let last_pointer_down = self.app_state.view_state(id).last_pointer_down.take(); + if let Some(action) = + self.get_event_listener(id, &EventListener::SecondaryClick) + { + if on_view && last_pointer_down.is_some() && (*action)(&event) { + return true; + } + } + + let viewport_event_position = { + let layout = self.app_state.view_state(id).layout_rect; + Point::new( + layout.x0 + pointer_event.pos.x, + layout.y0 + pointer_event.pos.y, + ) + }; + if let Some(menu) = &self.app_state.view_state(id).context_menu { + show_context_menu(menu(), Some(viewport_event_position)) + } + } + } + Event::KeyDown(_) => { + if self.app_state.is_focused(&id) && event.is_keyboard_trigger() { + if let Some(action) = self.get_event_listener(id, &EventListener::Click) { + (*action)(&event); + } + } + } + Event::WindowResized(_) => { + if let Some(view_state) = self.app_state.view_states.get(&id) { + if view_state.has_style_selectors.has_responsive() { + self.app_state.request_style(id); + } + } + } + _ => (), + } + + if let Some(listener) = event.listener() { + if let Some(action) = self.get_event_listener(id, &listener) { + let should_run = if let Some(pos) = event.point() { + let rect = self.get_size(id).unwrap_or_default().to_rect(); + rect.contains(pos) + } else { + true + }; + if should_run && (*action)(&event) { + return true; + } + } + } + + false + } + pub(crate) fn get_size(&self, id: Id) -> Option { self.app_state .get_layout(id) @@ -739,6 +1096,83 @@ 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) { + self.save(); + let id = view.id(); + let view_state = self.app_state_mut().view_state(id); + if !view_state.request_style { + return; + } + view_state.request_style = false; + + let view_style = view.view_style(); + let view_class = view.view_class(); + let class = view_state.class; + let class_array; + let classes = if let Some(class) = class { + class_array = [class]; + &class_array[..] + } else { + &[] + }; + + // Propagate style requests to children if needed. + if view_state.request_style_recursive { + view_state.request_style_recursive = false; + for child in view.children() { + let state = self.app_state_mut().view_state(child.id()); + state.request_style_recursive = true; + state.request_style = true; + } + } + + self.app_state + .compute_style(id, view_style, view_class, classes, &self.current); + let style = self.app_state_mut().get_computed_style(id).clone(); + self.direct = style; + Style::apply_only_inherited(&mut self.current, &self.direct); + CaptureState::capture_style(id, self); + + // If there's any changes to the Taffy style, request layout. + let taffy_style = self.direct.to_taffy_style(); + let view_state = self.app_state_mut().view_state(id); + if taffy_style != view_state.taffy_style { + view_state.taffy_style = taffy_style; + self.app_state_mut().request_layout(id); + } + + // This is used by the `request_transition` and `style` methods below. + self.current_view = id; + + let view_state = self.app_state.view_state(id); + + let mut transition = false; + + // Extract the relevant layout properties so the content rect can be calculated + // when painting. + view_state.layout_props.read_explicit( + &self.direct, + &self.current, + &self.now, + &mut transition, + ); + + view_state.view_style_props.read_explicit( + &self.direct, + &self.current, + &self.now, + &mut transition, + ); + if transition { + self.request_transition(); + } + + view.style(self); + + self.restore(); + } + pub fn save(&mut self) { self.saved.push(self.current.clone()); } @@ -874,6 +1308,100 @@ impl<'a> LayoutCx<'a> { node } + /// Internal method used by Floem to invoke the user-defined `View::layout` method. + pub fn layout_view(&mut self, view: &mut dyn View) -> Node { + self.save(); + let node = view.layout(self); + self.restore(); + node + } + + /// Internal method used by Floem. This method derives its calculations based on the [Taffy Node](taffy::prelude::Node) returned by the `View::layout` method. + /// + /// It's responsible for: + /// - calculating and setting the view's origin (local coordinates and window coordinates) + /// - calculating and setting the view's viewport + /// - 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) -> Rect { + let id = view.id(); + if self.app_state().is_hidden(id) { + return Rect::ZERO; + } + + self.save(); + + let layout = self + .app_state() + .get_layout(id) + .unwrap_or(taffy::layout::Layout::new()); + let origin = Point::new(layout.location.x as f64, layout.location.y as f64); + let parent_viewport = self.viewport.map(|rect| { + rect.with_origin(Point::new( + rect.x0 - layout.location.x as f64, + rect.y0 - layout.location.y as f64, + )) + }); + let viewport = self + .app_state() + .view_states + .get(&id) + .and_then(|view| view.viewport); + let size = Size::new(layout.size.width as f64, layout.size.height as f64); + match (parent_viewport, viewport) { + (Some(parent_viewport), Some(viewport)) => { + self.viewport = Some( + parent_viewport + .intersect(viewport) + .intersect(size.to_rect()), + ); + } + (Some(parent_viewport), None) => { + self.viewport = Some(parent_viewport.intersect(size.to_rect())); + } + (None, Some(viewport)) => { + self.viewport = Some(viewport.intersect(size.to_rect())); + } + (None, None) => { + self.viewport = None; + } + } + + let viewport = self.viewport.unwrap_or_default(); + let window_origin = origin + self.window_origin.to_vec2() - viewport.origin().to_vec2(); + self.window_origin = window_origin; + + if let Some(resize) = self.get_resize_listener(id) { + let new_rect = size.to_rect().with_origin(origin); + if new_rect != resize.rect { + resize.rect = new_rect; + (*resize.callback)(new_rect); + } + } + + if let Some(listener) = self.get_move_listener(id) { + if window_origin != listener.window_origin { + listener.window_origin = window_origin; + (*listener.callback)(window_origin); + } + } + + let child_layout_rect = view.compute_layout(self); + + let layout_rect = size.to_rect().with_origin(self.window_origin); + let layout_rect = if let Some(child_layout_rect) = child_layout_rect { + layout_rect.union(child_layout_rect) + } else { + layout_rect + }; + self.app_state_mut().view_state(id).layout_rect = layout_rect; + + self.restore(); + + layout_rect + } + pub(crate) fn get_resize_listener(&mut self, id: Id) -> Option<&mut ResizeListener> { self.app_state .view_states @@ -962,6 +1490,96 @@ impl<'a> PaintCx<'a> { } } + /// The entry point for painting a view. You shouldn't need to implement this yourself. Instead, implement [`View::paint`]. + /// It handles the internal work before and after painting [`View::paint`] implementations. + /// It is responsible for + /// - 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(); + if self.app_state.is_hidden(id) { + return; + } + + self.save(); + let size = self.transform(id); + let is_empty = self + .clip + .map(|rect| rect.rect().intersect(size.to_rect()).is_empty()) + .unwrap_or(false); + if !is_empty { + let style = self.app_state.get_computed_style(id).clone(); + let view_style_props = self.app_state.view_state(id).view_style_props.clone(); + + if let Some(z_index) = style.get(ZIndex) { + self.set_z_index(z_index); + } + + paint_bg(self, &style, &view_style_props, size); + + view.paint(self); + paint_border(self, &view_style_props, size); + paint_outline(self, &style, size) + } + + let mut drag_set_to_none = false; + if let Some(dragging) = self.app_state.dragging.as_ref() { + if dragging.id == id { + let dragging_offset = dragging.offset; + let mut offset_scale = None; + if let Some(released_at) = dragging.released_at { + const LIMIT: f64 = 300.0; + let elapsed = released_at.elapsed().as_millis() as f64; + if elapsed < LIMIT { + offset_scale = Some(1.0 - elapsed / LIMIT); + exec_after(std::time::Duration::from_millis(8), move |_| { + id.request_paint(); + }); + } else { + drag_set_to_none = true; + } + } else { + offset_scale = Some(1.0); + } + + if let Some(offset_scale) = offset_scale { + let offset = dragging_offset * offset_scale; + self.save(); + + let mut new = self.transform.as_coeffs(); + new[4] += offset.x; + new[5] += offset.y; + self.transform = Affine::new(new); + self.paint_state.renderer.transform(self.transform); + self.set_z_index(1000); + self.clear_clip(); + + let style = self.app_state.get_computed_style(id).clone(); + let view_state = self.app_state.view_state(id); + let view_style_props = view_state.view_style_props.clone(); + let style = if let Some(dragging_style) = view_state.dragging_style.clone() { + view_state.combined_style.clone().apply(dragging_style) + } else { + style + }; + paint_bg(self, &style, &view_style_props, size); + + view.paint(self); + paint_border(self, &view_style_props, size); + paint_outline(self, &style, size); + + self.restore(); + } + } + } + if drag_set_to_none { + self.app_state.dragging = None; + } + + self.restore(); + } + pub fn current_color(&self) -> Option { self.color } @@ -1124,6 +1742,26 @@ impl<'a> UpdateCx<'a> { pub fn request_layout(&mut self, id: Id) { self.app_state.request_layout(id); } + + /// 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, + ) -> ChangeFlags { + let id = id_path[0]; + let id_path = &id_path[1..]; + if id == view.id() { + if id_path.is_empty() { + return view.update(self, state); + } else if let Some(child) = view.child_mut(id_path[0]) { + return self.update_view(child, id_path, state); + } + } + ChangeFlags::empty() + } } impl Deref for PaintCx<'_> { diff --git a/src/view.rs b/src/view.rs index 041b75f2..bcb26ff0 100644 --- a/src/view.rs +++ b/src/view.rs @@ -83,20 +83,17 @@ //! //! -use std::any::Any; - use bitflags::bitflags; use floem_renderer::Renderer; -use kurbo::{Affine, Circle, Insets, Line, Point, Rect, RoundedRect, Size}; +use kurbo::{Circle, Insets, Line, Point, Rect, RoundedRect, Size}; +use std::any::Any; use taffy::prelude::Node; use crate::{ - action::{exec_after, show_context_menu}, - context::{AppState, DragState, EventCx, LayoutCx, PaintCx, StyleCx, UpdateCx, ViewStyleProps}, - event::{Event, EventListener}, + context::{AppState, EventCx, LayoutCx, PaintCx, StyleCx, UpdateCx, ViewStyleProps}, + event::Event, id::Id, - inspector::CaptureState, - style::{BoxShadowProp, Outline, OutlineColor, Style, StyleClassRef, ZIndex}, + style::{BoxShadowProp, Outline, OutlineColor, Style, StyleClassRef}, }; bitflags! { @@ -131,55 +128,10 @@ pub trait View { /// At the moment, this is used only to build the debug tree. fn children_mut(&mut self) -> Vec<&mut dyn View>; - fn cleanup(&mut self, app_state: &mut crate::context::AppState) { - for child in self.children_mut() { - child.cleanup(app_state); - } - let id = self.id(); - let view_state = app_state.view_state(id); - if let Some(action) = view_state.cleanup_listener.as_ref() { - action(); - } - let node = view_state.node; - if let Ok(children) = app_state.taffy.children(node) { - for child in children { - let _ = app_state.taffy.remove(child); - } - } - let _ = app_state.taffy.remove(node); - id.remove_id_path(); - app_state.view_states.remove(&id); - if app_state.focus == Some(id) { - app_state.focus = None; - } - } - fn debug_name(&self) -> std::borrow::Cow<'static, str> { core::any::type_name::().into() } - /// 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. - /// - /// You shouldn't need to implement this. - fn update_main( - &mut self, - cx: &mut UpdateCx, - id_path: &[Id], - state: Box, - ) -> ChangeFlags { - let id = id_path[0]; - let id_path = &id_path[1..]; - if id == self.id() { - if id_path.is_empty() { - return self.update(cx, state); - } else if let Some(child) = self.child_mut(id_path[0]) { - return child.update_main(cx, id_path, state); - } - } - ChangeFlags::empty() - } - /// Use this method to react to changes in view-related state. /// You will usually send state to this hook manually using the `View`'s `Id` handle /// @@ -191,192 +143,17 @@ pub trait View { /// indicating if you'd like a layout or paint pass to be scheduled. fn update(&mut self, cx: &mut UpdateCx, state: Box) -> ChangeFlags; - /// Internal method used by Floem to compute the styles for the view. - /// - /// You shouldn't need to implement this. - fn style_main(&mut self, cx: &mut StyleCx<'_>) { - cx.save(); - - let view_state = cx.app_state_mut().view_state(self.id()); - if !view_state.request_style { - return; - } - view_state.request_style = false; - - let view_style = self.view_style(); - let view_class = self.view_class(); - let class = view_state.class; - let class_array; - let classes = if let Some(class) = class { - class_array = [class]; - &class_array[..] - } else { - &[] - }; - - // Propagate style requests to children if needed. - if view_state.request_style_recursive { - view_state.request_style_recursive = false; - for child in self.children() { - let state = cx.app_state_mut().view_state(child.id()); - state.request_style_recursive = true; - state.request_style = true; - } - } - - cx.app_state - .compute_style(self.id(), view_style, view_class, classes, &cx.current); - let style = cx.app_state_mut().get_computed_style(self.id()).clone(); - cx.direct = style; - Style::apply_only_inherited(&mut cx.current, &cx.direct); - CaptureState::capture_style(self.id(), cx); - - // If there's any changes to the Taffy style, request layout. - let taffy_style = cx.direct.to_taffy_style(); - let view_state = cx.app_state_mut().view_state(self.id()); - if taffy_style != view_state.taffy_style { - view_state.taffy_style = taffy_style; - cx.app_state_mut().request_layout(self.id()); - } - - // This is used by the `request_transition` and `style` methods below. - cx.current_view = self.id(); - - let view_state = cx.app_state.view_state(self.id()); - - let mut transition = false; - - // Extract the relevant layout properties so the content rect can be calculated - // when painting. - view_state - .layout_props - .read_explicit(&cx.direct, &cx.current, &cx.now, &mut transition); - - view_state.view_style_props.read_explicit( - &cx.direct, - &cx.current, - &cx.now, - &mut transition, - ); - if transition { - cx.request_transition(); - } - - self.style(cx); - - cx.restore(); - } - /// Use this method to style the view's children. fn style(&mut self, cx: &mut StyleCx<'_>) { for child in self.children_mut() { - child.style_main(cx) + cx.style_view(child) } } - /// Internal method used by Floem to invoke the user-defined `View::layout` method. - /// - /// You shouldn't need to implement this. - fn layout_main(&mut self, cx: &mut LayoutCx) -> Node { - cx.save(); - - let node = self.layout(cx); - - cx.restore(); - node - } - /// Use this method to layout the view's children. /// Usually you'll do this by calling `LayoutCx::layout_node` fn layout(&mut self, cx: &mut LayoutCx) -> Node; - /// Internal method used by Floem. This method derives its calculations based on the [Taffy Node](taffy::prelude::Node) returned by the `View::layout` method. - /// - /// It's responsible for: - /// - calculating and setting the view's origin (local coordinates and window coordinates) - /// - calculating and setting the view's viewport - /// - invoking any attached context::ResizeListeners - /// - /// Returns the bounding rect that encompasses this view and its children - /// - /// You shouldn't need to implement this. - fn compute_layout_main(&mut self, cx: &mut LayoutCx) -> Rect { - if cx.app_state().is_hidden(self.id()) { - return Rect::ZERO; - } - - cx.save(); - - let layout = cx - .app_state() - .get_layout(self.id()) - .unwrap_or(taffy::layout::Layout::new()); - let origin = Point::new(layout.location.x as f64, layout.location.y as f64); - let parent_viewport = cx.viewport.map(|rect| { - rect.with_origin(Point::new( - rect.x0 - layout.location.x as f64, - rect.y0 - layout.location.y as f64, - )) - }); - let viewport = cx - .app_state() - .view_states - .get(&self.id()) - .and_then(|view| view.viewport); - let size = Size::new(layout.size.width as f64, layout.size.height as f64); - match (parent_viewport, viewport) { - (Some(parent_viewport), Some(viewport)) => { - cx.viewport = Some( - parent_viewport - .intersect(viewport) - .intersect(size.to_rect()), - ); - } - (Some(parent_viewport), None) => { - cx.viewport = Some(parent_viewport.intersect(size.to_rect())); - } - (None, Some(viewport)) => { - cx.viewport = Some(viewport.intersect(size.to_rect())); - } - (None, None) => { - cx.viewport = None; - } - } - - let viewport = cx.viewport.unwrap_or_default(); - let window_origin = origin + cx.window_origin.to_vec2() - viewport.origin().to_vec2(); - cx.window_origin = window_origin; - - if let Some(resize) = cx.get_resize_listener(self.id()) { - let new_rect = size.to_rect().with_origin(origin); - if new_rect != resize.rect { - resize.rect = new_rect; - (*resize.callback)(new_rect); - } - } - - if let Some(listener) = cx.get_move_listener(self.id()) { - if window_origin != listener.window_origin { - listener.window_origin = window_origin; - (*listener.callback)(window_origin); - } - } - - let child_layout_rect = self.compute_layout(cx); - - let layout_rect = size.to_rect().with_origin(cx.window_origin); - let layout_rect = if let Some(child_layout_rect) = child_layout_rect { - layout_rect.union(child_layout_rect) - } else { - layout_rect - }; - cx.app_state_mut().view_state(self.id()).layout_rect = layout_rect; - - cx.restore(); - - layout_rect - } - /// You must implement this if your view has children. /// /// Responsible for computing the layout of the view's children. @@ -384,404 +161,22 @@ pub trait View { None } - /// Internal method used by Floem. This can be called from parent `View`s to propagate an event to the child `View`. - /// - /// You shouldn't need to implement this. - fn event_main(&mut self, cx: &mut EventCx, id_path: Option<&[Id]>, event: Event) -> bool { - let id = self.id(); - if cx.app_state.is_hidden(id) { - // we don't process events for hidden view - return false; - } - if cx.app_state.is_disabled(&id) && !event.allow_disabled() { - // if the view is disabled and the event is not processed - // for disabled views - return false; - } - - // offset the event positions if the event has positions - // e.g. pointer events, so that the position is relative - // to the view, taking into account of the layout location - // of the view and the viewport of the view if it's in a scroll. - let event = cx.offset_event(self.id(), event); - - // if there's id_path, it's an event only for a view. - if let Some(id_path) = id_path { - if id_path.is_empty() { - // this happens when the parent is the destination, - // but the parent just passed the event on, - // so it's not really for this view and we stop - // the event propagation. - return false; - } - - let id = id_path[0]; - let id_path = &id_path[1..]; - - if id != self.id() { - // This shouldn't happen - return false; - } - - // we're the parent of the event destination, so pass it on to the child - if !id_path.is_empty() { - if let Some(child) = self.child_mut(id_path[0]) { - return child.event_main(cx, Some(id_path), event.clone()); - } else { - // we don't have the child, stop the event propagation - return false; - } - } - } - - // if the event was dispatched to an id_path, the event is supposed to be only - // handled by this view only, so we pass an empty id_path - // and the event propagation would be stopped at this view - if self.event( - cx, - if id_path.is_some() { Some(&[]) } else { None }, - event.clone(), - ) { - return true; - } - - match &event { - Event::PointerDown(event) => { - cx.app_state.clicking.insert(self.id()); - if event.button.is_primary() { - let rect = cx.get_size(self.id()).unwrap_or_default().to_rect(); - let now_focused = rect.contains(event.pos); - - if now_focused { - if cx.app_state.keyboard_navigable.contains(&id) { - // if the view can be focused, we update the focus - cx.app_state.update_focus(id, false); - } - if event.count == 2 && cx.has_event_listener(id, EventListener::DoubleClick) - { - let view_state = cx.app_state.view_state(id); - view_state.last_pointer_down = Some(event.clone()); - } - if cx.has_event_listener(id, EventListener::Click) { - let view_state = cx.app_state.view_state(id); - view_state.last_pointer_down = Some(event.clone()); - } - - let bottom_left = { - let layout = cx.app_state.view_state(id).layout_rect; - Point::new(layout.x0, layout.y1) - }; - if let Some(menu) = &cx.app_state.view_state(id).popout_menu { - show_context_menu(menu(), Some(bottom_left)) - } - if cx.app_state.draggable.contains(&id) && cx.app_state.drag_start.is_none() - { - cx.app_state.drag_start = Some((id, event.pos)); - } - } - } else if event.button.is_secondary() { - let rect = cx.get_size(self.id()).unwrap_or_default().to_rect(); - let now_focused = rect.contains(event.pos); - - if now_focused { - if cx.app_state.keyboard_navigable.contains(&id) { - // if the view can be focused, we update the focus - cx.app_state.update_focus(id, false); - } - if cx.has_event_listener(id, EventListener::SecondaryClick) { - let view_state = cx.app_state.view_state(id); - view_state.last_pointer_down = Some(event.clone()); - } - } - } - } - Event::PointerMove(pointer_event) => { - let rect = cx.get_size(id).unwrap_or_default().to_rect(); - if rect.contains(pointer_event.pos) { - if cx.app_state.is_dragging() { - cx.app_state.dragging_over.insert(id); - if let Some(action) = cx.get_event_listener(id, &EventListener::DragOver) { - (*action)(&event); - } - } else { - cx.app_state.hovered.insert(id); - let style = cx.app_state.get_builtin_style(id); - if let Some(cursor) = style.cursor() { - if cx.app_state.cursor.is_none() { - cx.app_state.cursor = Some(cursor); - } - } - } - } - if cx.app_state.draggable.contains(&id) { - if let Some((_, drag_start)) = cx - .app_state - .drag_start - .as_ref() - .filter(|(drag_id, _)| drag_id == &id) - { - let vec2 = pointer_event.pos - *drag_start; - - if let Some(dragging) = cx - .app_state - .dragging - .as_mut() - .filter(|d| d.id == id && d.released_at.is_none()) - { - // update the dragging offset if the view is dragging and not released - dragging.offset = vec2; - id.request_paint(); - } else if vec2.x.abs() + vec2.y.abs() > 1.0 { - // start dragging when moved 1 px - cx.app_state.active = None; - cx.update_active(id); - cx.app_state.dragging = Some(DragState { - id, - offset: vec2, - released_at: None, - }); - id.request_paint(); - if let Some(action) = - cx.get_event_listener(id, &EventListener::DragStart) - { - (*action)(&event); - } - } - } - } - if let Some(action) = cx.get_event_listener(id, &EventListener::PointerMove) { - if (*action)(&event) { - return true; - } - } - } - Event::PointerUp(pointer_event) => { - if pointer_event.button.is_primary() { - let rect = cx.get_size(self.id()).unwrap_or_default().to_rect(); - let on_view = rect.contains(pointer_event.pos); - - if id_path.is_none() { - if on_view { - if let Some(dragging) = cx.app_state.dragging.as_mut() { - let dragging_id = dragging.id; - if let Some(action) = - cx.get_event_listener(id, &EventListener::Drop) - { - if (*action)(&event) { - // if the drop is processed, we set dragging to none so that the animation - // for the dragged view back to its original position isn't played. - cx.app_state.dragging = None; - id.request_paint(); - if let Some(action) = cx.get_event_listener( - dragging_id, - &EventListener::DragEnd, - ) { - (*action)(&event); - } - } - } - } - } - } else if let Some(dragging) = - cx.app_state.dragging.as_mut().filter(|d| d.id == id) - { - let dragging_id = dragging.id; - dragging.released_at = Some(std::time::Instant::now()); - id.request_paint(); - if let Some(action) = - cx.get_event_listener(dragging_id, &EventListener::DragEnd) - { - (*action)(&event); - } - } - - let last_pointer_down = cx.app_state.view_state(id).last_pointer_down.take(); - if let Some(action) = cx.get_event_listener(id, &EventListener::DoubleClick) { - if on_view - && cx.app_state.is_clicking(&id) - && last_pointer_down - .as_ref() - .map(|e| e.count == 2) - .unwrap_or(false) - && (*action)(&event) - { - return true; - } - } - if let Some(action) = cx.get_event_listener(id, &EventListener::Click) { - if on_view - && cx.app_state.is_clicking(&id) - && last_pointer_down.is_some() - && (*action)(&event) - { - return true; - } - } - - if let Some(action) = cx.get_event_listener(id, &EventListener::PointerUp) { - if (*action)(&event) { - return true; - } - } - } else if pointer_event.button.is_secondary() { - let rect = cx.get_size(self.id()).unwrap_or_default().to_rect(); - let on_view = rect.contains(pointer_event.pos); - - let last_pointer_down = cx.app_state.view_state(id).last_pointer_down.take(); - if let Some(action) = cx.get_event_listener(id, &EventListener::SecondaryClick) - { - if on_view && last_pointer_down.is_some() && (*action)(&event) { - return true; - } - } - - let viewport_event_position = { - let layout = cx.app_state.view_state(id).layout_rect; - Point::new( - layout.x0 + pointer_event.pos.x, - layout.y0 + pointer_event.pos.y, - ) - }; - if let Some(menu) = &cx.app_state.view_state(id).context_menu { - show_context_menu(menu(), Some(viewport_event_position)) - } - } - } - Event::KeyDown(_) => { - if cx.app_state.is_focused(&id) && event.is_keyboard_trigger() { - if let Some(action) = cx.get_event_listener(id, &EventListener::Click) { - (*action)(&event); - } - } - } - Event::WindowResized(_) => { - if let Some(view_state) = cx.app_state.view_states.get(&self.id()) { - if view_state.has_style_selectors.has_responsive() { - cx.app_state.request_style(self.id()); - } - } - } - _ => (), - } - - if let Some(listener) = event.listener() { - if let Some(action) = cx.get_event_listener(self.id(), &listener) { - let should_run = if let Some(pos) = event.point() { - let rect = cx.get_size(self.id()).unwrap_or_default().to_rect(); - rect.contains(pos) - } else { - true - }; - if should_run && (*action)(&event) { - return true; - } - } - } - - false - } - /// Implement this to handle events and to pass them down to children /// /// Return true to stop the event from propagating to other views fn event(&mut self, cx: &mut EventCx, id_path: Option<&[Id]>, event: Event) -> bool; - /// The entry point for painting a view. You shouldn't need to implement this yourself. Instead, implement [`View::paint`]. - /// It handles the internal work before and after painting [`View::paint`] implementations. - /// It is responsible for - /// - managing hidden status - /// - clipping - /// - painting computed styles like background color, border, font-styles, and z-index and handling painting requirements of drag and drop - fn paint_main(&mut self, cx: &mut PaintCx) { - let id = self.id(); - if cx.app_state.is_hidden(id) { - return; - } - - cx.save(); - let size = cx.transform(id); - let is_empty = cx - .clip - .map(|rect| rect.rect().intersect(size.to_rect()).is_empty()) - .unwrap_or(false); - if !is_empty { - let style = cx.app_state.get_computed_style(id).clone(); - let view_style_props = cx.app_state.view_state(id).view_style_props.clone(); - - if let Some(z_index) = style.get(ZIndex) { - cx.set_z_index(z_index); - } - - paint_bg(cx, &style, &view_style_props, size); - - self.paint(cx); - paint_border(cx, &view_style_props, size); - paint_outline(cx, &style, size) - } - - let mut drag_set_to_none = false; - if let Some(dragging) = cx.app_state.dragging.as_ref() { - if dragging.id == id { - let dragging_offset = dragging.offset; - let mut offset_scale = None; - if let Some(released_at) = dragging.released_at { - const LIMIT: f64 = 300.0; - let elapsed = released_at.elapsed().as_millis() as f64; - if elapsed < LIMIT { - offset_scale = Some(1.0 - elapsed / LIMIT); - exec_after(std::time::Duration::from_millis(8), move |_| { - id.request_paint(); - }); - } else { - drag_set_to_none = true; - } - } else { - offset_scale = Some(1.0); - } - - if let Some(offset_scale) = offset_scale { - let offset = dragging_offset * offset_scale; - cx.save(); - - let mut new = cx.transform.as_coeffs(); - new[4] += offset.x; - new[5] += offset.y; - cx.transform = Affine::new(new); - cx.paint_state.renderer.transform(cx.transform); - cx.set_z_index(1000); - cx.clear_clip(); - - let style = cx.app_state.get_computed_style(id).clone(); - let view_state = cx.app_state.view_state(id); - let view_style_props = view_state.view_style_props.clone(); - let style = if let Some(dragging_style) = view_state.dragging_style.clone() { - view_state.combined_style.clone().apply(dragging_style) - } else { - style - }; - paint_bg(cx, &style, &view_style_props, size); - - self.paint(cx); - paint_border(cx, &view_style_props, size); - paint_outline(cx, &style, size); - - cx.restore(); - } - } - } - if drag_set_to_none { - cx.app_state.dragging = None; - } - - cx.restore(); - } - /// `View`-specific implementation. Will be called in the [`View::paint_main`] entry point method. /// Usually you'll call the child `View::paint_main` method. But you might also draw text, adjust the offset, clip or draw text. fn paint(&mut self, cx: &mut PaintCx); } -fn paint_bg(cx: &mut PaintCx, computed_style: &Style, style: &ViewStyleProps, size: Size) { +pub(crate) fn paint_bg( + cx: &mut PaintCx, + computed_style: &Style, + style: &ViewStyleProps, + size: Size, +) { let radius = style.border_radius().0; if radius > 0.0 { let rect = size.to_rect(); @@ -832,7 +227,7 @@ fn paint_box_shadow(cx: &mut PaintCx, style: &Style, rect: Rect, rect_radius: Op } } -fn paint_outline(cx: &mut PaintCx, style: &Style, size: Size) { +pub(crate) fn paint_outline(cx: &mut PaintCx, style: &Style, size: Size) { let outline = style.get(Outline).0; if outline == 0. { // TODO: we should warn! when outline is < 0 @@ -843,7 +238,7 @@ fn paint_outline(cx: &mut PaintCx, style: &Style, size: Size) { cx.stroke(&rect, style.get(OutlineColor), outline); } -fn paint_border(cx: &mut PaintCx, style: &ViewStyleProps, size: Size) { +pub(crate) fn paint_border(cx: &mut PaintCx, style: &ViewStyleProps, size: Size) { let left = style.border_left().0; let top = style.border_top().0; let right = style.border_right().0; diff --git a/src/views/clip.rs b/src/views/clip.rs index c82ddcf0..5a88b956 100644 --- a/src/views/clip.rs +++ b/src/views/clip.rs @@ -59,11 +59,11 @@ impl View for Clip { } 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)]) + cx.layout_node(self.id, true, |cx| vec![cx.layout_view(&mut self.child)]) } fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { - Some(self.child.compute_layout_main(cx)) + Some(cx.compute_view_layout(&mut self.child)) } fn event( @@ -72,11 +72,7 @@ impl View for Clip { id_path: Option<&[Id]>, event: crate::event::Event, ) -> bool { - if cx.should_send(self.child.id(), &event) { - self.child.event_main(cx, id_path, event) - } else { - false - } + cx.view_event(&mut self.child, id_path, event) } fn paint(&mut self, cx: &mut crate::context::PaintCx) { @@ -93,7 +89,7 @@ impl View for Clip { } else { cx.clip(&size.to_rect()); } - self.child.paint_main(cx); + cx.paint_view(&mut self.child); cx.restore(); } } diff --git a/src/views/container.rs b/src/views/container.rs index aca9b488..6bc0cd83 100644 --- a/src/views/container.rs +++ b/src/views/container.rs @@ -64,11 +64,11 @@ impl View for Container { } 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)]) + cx.layout_node(self.id, true, |cx| vec![cx.layout_view(&mut self.child)]) } fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { - Some(self.child.compute_layout_main(cx)) + Some(cx.compute_view_layout(&mut self.child)) } fn event( @@ -77,14 +77,10 @@ impl View for Container { id_path: Option<&[Id]>, event: crate::event::Event, ) -> bool { - if cx.should_send(self.child.id(), &event) { - self.child.event_main(cx, id_path, event) - } else { - false - } + cx.view_event(&mut self.child, id_path, event) } fn paint(&mut self, cx: &mut crate::context::PaintCx) { - self.child.paint_main(cx); + cx.paint_view(&mut self.child); } } diff --git a/src/views/container_box.rs b/src/views/container_box.rs index 43ac296a..4b58efcf 100644 --- a/src/views/container_box.rs +++ b/src/views/container_box.rs @@ -94,11 +94,11 @@ impl View for ContainerBox { } 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)]) + cx.layout_node(self.id, true, |cx| vec![cx.layout_view(&mut self.child)]) } fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { - Some(self.child.compute_layout_main(cx)) + Some(cx.compute_view_layout(&mut self.child)) } fn event( @@ -107,14 +107,10 @@ impl View for ContainerBox { id_path: Option<&[Id]>, event: crate::event::Event, ) -> bool { - if cx.should_send(self.child.id(), &event) { - self.child.event_main(cx, id_path, event) - } else { - false - } + cx.view_event(&mut self.child, id_path, event) } fn paint(&mut self, cx: &mut crate::context::PaintCx) { - self.child.paint_main(cx); + cx.paint_view(&mut self.child); } } diff --git a/src/views/drag_resize_window_area.rs b/src/views/drag_resize_window_area.rs index ebb03e35..aee3353b 100644 --- a/src/views/drag_resize_window_area.rs +++ b/src/views/drag_resize_window_area.rs @@ -79,11 +79,11 @@ impl View for DragResizeWindowArea { } 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)]) + cx.layout_node(self.id, true, |cx| vec![cx.layout_view(&mut self.child)]) } fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { - Some(self.child.compute_layout_main(cx)) + Some(cx.compute_view_layout(&mut self.child)) } fn event( @@ -92,14 +92,10 @@ impl View for DragResizeWindowArea { id_path: Option<&[Id]>, event: Event, ) -> bool { - if cx.should_send(self.child.id(), &event) { - self.child.event_main(cx, id_path, event) - } else { - false - } + cx.view_event(&mut self.child, id_path, event) } fn paint(&mut self, cx: &mut crate::context::PaintCx) { - self.child.paint_main(cx); + cx.paint_view(&mut self.child); } } diff --git a/src/views/drag_window_area.rs b/src/views/drag_window_area.rs index 0b2b5ad4..97412ae2 100644 --- a/src/views/drag_window_area.rs +++ b/src/views/drag_window_area.rs @@ -9,14 +9,14 @@ use crate::{ use super::Decorators; -pub struct DargWindowArea { +pub struct DragWindowArea { id: Id, child: V, } -pub fn drag_window_area(child: V) -> DargWindowArea { +pub fn drag_window_area(child: V) -> DragWindowArea { let id = Id::next(); - DargWindowArea { id, child } + DragWindowArea { id, child } .on_event(EventListener::PointerDown, |_| { drag_window(); true @@ -27,7 +27,7 @@ pub fn drag_window_area(child: V) -> DargWindowArea { }) } -impl View for DargWindowArea { +impl View for DragWindowArea { fn id(&self) -> Id { self.id } @@ -65,11 +65,11 @@ impl View for DargWindowArea { } 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)]) + cx.layout_node(self.id, true, |cx| vec![cx.layout_view(&mut self.child)]) } fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { - Some(self.child.compute_layout_main(cx)) + Some(cx.compute_view_layout(&mut self.child)) } fn event( @@ -78,14 +78,10 @@ impl View for DargWindowArea { id_path: Option<&[Id]>, event: Event, ) -> bool { - if cx.should_send(self.child.id(), &event) { - self.child.event_main(cx, id_path, event) - } else { - false - } + cx.view_event(&mut self.child, id_path, event) } fn paint(&mut self, cx: &mut crate::context::PaintCx) { - self.child.paint_main(cx); + cx.paint_view(&mut self.child); } } diff --git a/src/views/dyn_container.rs b/src/views/dyn_container.rs index 1f31ceb2..83a87f6e 100644 --- a/src/views/dyn_container.rs +++ b/src/views/dyn_container.rs @@ -140,11 +140,11 @@ impl View for DynamicContainer { } 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)]) + cx.layout_node(self.id, true, |cx| vec![cx.layout_view(&mut self.child)]) } fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { - Some(self.child.compute_layout_main(cx)) + Some(cx.compute_view_layout(&mut self.child)) } fn event( @@ -153,14 +153,10 @@ impl View for DynamicContainer { id_path: Option<&[Id]>, event: crate::event::Event, ) -> bool { - if cx.should_send(self.child.id(), &event) { - self.child.event_main(cx, id_path, event) - } else { - false - } + cx.view_event(&mut self.child, id_path, event) } fn paint(&mut self, cx: &mut crate::context::PaintCx) { - self.child.paint_main(cx); + cx.paint_view(&mut self.child); } } diff --git a/src/views/list.rs b/src/views/list.rs index 231d47e7..2c937981 100644 --- a/src/views/list.rs +++ b/src/views/list.rs @@ -152,7 +152,7 @@ impl View for List { let nodes = self .children .iter_mut() - .filter_map(|child| Some(child.as_mut()?.0.layout_main(cx))) + .filter_map(|child| Some(cx.layout_view(&mut child.as_mut()?.0))) .collect::>(); nodes }) @@ -162,7 +162,7 @@ impl View for List { let mut layout_rect = Rect::ZERO; for child in &mut self.children { if let Some((child, _)) = child.as_mut() { - layout_rect = layout_rect.union(child.compute_layout_main(cx)); + layout_rect = layout_rect.union(cx.compute_view_layout(child)); } } Some(layout_rect) @@ -176,8 +176,7 @@ impl View for List { ) -> bool { for child in self.children.iter_mut() { if let Some((child, _)) = child.as_mut() { - let id = child.id(); - if cx.should_send(id, &event) && child.event_main(cx, id_path, event.clone()) { + if cx.view_event(child, id_path, event.clone()) { return true; } } @@ -188,7 +187,7 @@ impl View for List { fn paint(&mut self, cx: &mut crate::context::PaintCx) { for child in self.children.iter_mut() { if let Some((child, _)) = child.as_mut() { - child.paint_main(cx); + cx.paint_view(child); } } } @@ -325,7 +324,7 @@ fn remove_index( index: usize, ) -> Option<()> { let (mut view, scope) = std::mem::take(&mut children[index])?; - view.cleanup(app_state); + app_state.remove_view(&mut view); scope.dispose(); Some(()) } diff --git a/src/views/scroll.rs b/src/views/scroll.rs index 3582c99b..551af54e 100644 --- a/src/views/scroll.rs +++ b/src/views/scroll.rs @@ -640,7 +640,7 @@ impl View for Scroll { self.track_hover_style .read_style(cx, &track_style.apply_selectors(&[StyleSelector::Hover])); - self.child.style_main(cx); + cx.style_view(&mut self.child); } fn layout(&mut self, cx: &mut crate::context::LayoutCx) -> taffy::prelude::Node { @@ -651,7 +651,7 @@ impl View for Scroll { .combined_style .clone() .set(PositionProp, Position::Absolute); - let child_node = self.child.layout_main(cx); + let child_node = cx.layout_view(&mut self.child); let virtual_style = Style::new() .width(self.child_size.width) @@ -680,7 +680,7 @@ impl View for Scroll { fn compute_layout(&mut self, cx: &mut LayoutCx) -> Option { self.update_size(cx.app_state_mut()); self.clamp_child_viewport(cx.app_state_mut(), self.child_viewport); - self.child.compute_layout_main(cx); + cx.compute_view_layout(&mut self.child); None } @@ -797,9 +797,7 @@ impl View for Scroll { _ => {} } - if cx.should_send(self.child.id(), &event) - && self.child.event_main(cx, id_path, event.clone()) - { + if cx.view_event(&mut self.child, id_path, event.clone()) { return true; } @@ -839,7 +837,7 @@ impl View for Scroll { cx.clip(&self.actual_rect); } cx.offset((-self.child_viewport.x0, -self.child_viewport.y0)); - self.child.paint_main(cx); + cx.paint_view(&mut self.child); cx.restore(); if !self.hide { diff --git a/src/views/stack.rs b/src/views/stack.rs index 48ba4540..7f4680e5 100644 --- a/src/views/stack.rs +++ b/src/views/stack.rs @@ -90,7 +90,7 @@ impl View for Stack { fn style(&mut self, cx: &mut crate::context::StyleCx) { self.children.foreach_mut(&mut |view| { - view.style_main(cx); + cx.style_view(view); false }); } @@ -103,14 +103,8 @@ impl View for Stack { ) -> bool { let mut handled = false; self.children.foreach_rev(&mut |view| { - let id = view.id(); - if cx.should_send(id, &event) { - handled = view.event_main(cx, id_path, event.clone()); - if handled { - return true; - } - } - false + handled |= cx.view_event(view, id_path, event.clone()); + handled }); handled } @@ -119,7 +113,7 @@ impl View for Stack { cx.layout_node(self.id, true, |cx| { let mut nodes = Vec::new(); self.children.foreach_mut(&mut |view| { - let node = view.layout_main(cx); + let node = cx.layout_view(view); nodes.push(node); false }); @@ -130,7 +124,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(&mut |view| { - layout_rect = layout_rect.union(view.compute_layout_main(cx)); + layout_rect = layout_rect.union(cx.compute_view_layout(view)); false }); Some(layout_rect) @@ -138,7 +132,7 @@ impl View for Stack { fn paint(&mut self, cx: &mut crate::context::PaintCx) { self.children.foreach_mut(&mut |view| { - view.paint_main(cx); + cx.paint_view(view); false }); } diff --git a/src/views/static_list.rs b/src/views/static_list.rs index 66eb421f..8738b310 100644 --- a/src/views/static_list.rs +++ b/src/views/static_list.rs @@ -76,7 +76,7 @@ impl View for StaticList { fn style(&mut self, cx: &mut crate::context::StyleCx) { for child in &mut self.children { - child.style_main(cx); + cx.style_view(child); } } @@ -85,7 +85,7 @@ impl View for StaticList { let nodes = self .children .iter_mut() - .map(|child| child.layout_main(cx)) + .map(|child| cx.layout_view(child)) .collect::>(); nodes }) @@ -94,7 +94,7 @@ impl View for StaticList { fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option { let mut layout_rect = Rect::ZERO; for child in &mut self.children { - layout_rect = layout_rect.union(child.compute_layout_main(cx)); + layout_rect = layout_rect.union(cx.compute_view_layout(child)); } Some(layout_rect) } @@ -106,8 +106,7 @@ impl View for StaticList { event: crate::event::Event, ) -> bool { for child in self.children.iter_mut() { - let id = child.id(); - if cx.should_send(id, &event) && child.event_main(cx, id_path, event.clone()) { + if cx.view_event(child, id_path, event.clone()) { return true; } } @@ -116,7 +115,7 @@ impl View for StaticList { fn paint(&mut self, cx: &mut crate::context::PaintCx) { for child in self.children.iter_mut() { - child.paint_main(cx); + cx.paint_view(child); } } } diff --git a/src/views/tab.rs b/src/views/tab.rs index 1c4718eb..0ca6a75a 100644 --- a/src/views/tab.rs +++ b/src/views/tab.rs @@ -189,7 +189,7 @@ impl View for Tab { Display::Flex }, ); - let node = child.as_mut()?.0.layout_main(cx); + let node = cx.layout_view(&mut child.as_mut()?.0); Some(node) }) .collect::>(); @@ -201,7 +201,7 @@ impl View for Tab { let mut layout_rect = Rect::ZERO; for child in &mut self.children { if let Some((child, _)) = child.as_mut() { - layout_rect = layout_rect.union(child.compute_layout_main(cx)); + layout_rect = layout_rect.union(cx.compute_view_layout(child)); } } Some(layout_rect) @@ -214,11 +214,7 @@ impl View for Tab { event: crate::event::Event, ) -> bool { if let Some(Some((child, _))) = self.children.get_mut(self.active) { - if cx.should_send(child.id(), &event) { - child.event_main(cx, id_path, event) - } else { - false - } + cx.view_event(child, id_path, event) } else { false } @@ -226,7 +222,7 @@ impl View for Tab { fn paint(&mut self, cx: &mut crate::context::PaintCx) { if let Some(Some((child, _))) = self.children.get_mut(self.active) { - child.paint_main(cx); + cx.paint_view(child); } } } diff --git a/src/views/virtual_list.rs b/src/views/virtual_list.rs index 16ff9390..86e39834 100644 --- a/src/views/virtual_list.rs +++ b/src/views/virtual_list.rs @@ -278,7 +278,7 @@ impl View for VirtualList { let mut nodes = self .children .iter_mut() - .filter_map(|child| Some(child.as_mut()?.0.layout_main(cx))) + .filter_map(|child| Some(cx.layout_view(&mut child.as_mut()?.0))) .collect::>(); let before_size = match self.direction { VirtualListDirection::Vertical => taffy::prelude::Size { @@ -351,7 +351,7 @@ impl View for VirtualList { let mut layout_rect = Rect::ZERO; for child in &mut self.children { if let Some((child, _)) = child.as_mut() { - layout_rect = layout_rect.union(child.compute_layout_main(cx)); + layout_rect = layout_rect.union(cx.compute_view_layout(child)); } } Some(layout_rect) @@ -365,8 +365,7 @@ impl View for VirtualList { ) -> bool { for child in self.children.iter_mut() { if let Some((child, _)) = child.as_mut() { - let id = child.id(); - if cx.should_send(id, &event) && child.event_main(cx, id_path, event.clone()) { + if cx.view_event(child, id_path, event.clone()) { return true; } } @@ -377,7 +376,7 @@ impl View for VirtualList { fn paint(&mut self, cx: &mut crate::context::PaintCx) { for child in &mut self.children { if let Some((child, _)) = child.as_mut() { - child.paint_main(cx); + cx.paint_view(child); } } } diff --git a/src/window_handle.rs b/src/window_handle.rs index 09fa9db3..c320b421 100644 --- a/src/window_handle.rs +++ b/src/window_handle.rs @@ -170,9 +170,11 @@ impl WindowHandle { if let Some(id) = cx.app_state.focus { let id_path = ID_PATHS.with(|paths| paths.borrow().get(&id).cloned()); if let Some(id_path) = id_path { - processed |= - self.view - .event_main(&mut cx, Some(id_path.dispatch()), event.clone()); + processed |= cx.unconditional_view_event( + &mut self.view, + Some(id_path.dispatch()), + event.clone(), + ); } else { cx.app_state.focus = None; } @@ -217,14 +219,17 @@ impl WindowHandle { } } else if cx.app_state.active.is_some() && event.is_pointer() { if cx.app_state.is_dragging() { - self.view.event_main(&mut cx, None, event.clone()); + cx.unconditional_view_event(&mut self.view, None, event.clone()); } let id = cx.app_state.active.unwrap(); let id_path = ID_PATHS.with(|paths| paths.borrow().get(&id).cloned()); if let Some(id_path) = id_path { - self.view - .event_main(&mut cx, Some(id_path.dispatch()), event.clone()); + cx.unconditional_view_event( + &mut self.view, + Some(id_path.dispatch()), + event.clone(), + ); } if let Event::PointerUp(_) = &event { // To remove the styles applied by the Active selector @@ -235,7 +240,7 @@ impl WindowHandle { cx.app_state.active = None; } } else { - self.view.event_main(&mut cx, None, event.clone()); + cx.unconditional_view_event(&mut self.view, None, event.clone()); } if let Event::PointerUp(_) = &event { @@ -258,8 +263,8 @@ impl WindowHandle { } else { let id_path = ID_PATHS.with(|paths| paths.borrow().get(id).cloned()); if let Some(id_path) = id_path { - self.view.event_main( - &mut cx, + cx.unconditional_view_event( + &mut self.view, Some(id_path.dispatch()), Event::PointerLeave, ); @@ -380,8 +385,11 @@ impl WindowHandle { } let id_path = ID_PATHS.with(|paths| paths.borrow().get(&id).cloned()); if let Some(id_path) = id_path { - self.view - .event_main(&mut cx, Some(id_path.dispatch()), Event::PointerLeave); + cx.unconditional_view_event( + &mut self.view, + Some(id_path.dispatch()), + Event::PointerLeave, + ); } } self.process_update(); @@ -452,20 +460,20 @@ impl WindowHandle { fn style(&mut self) { let mut cx = StyleCx::new(&mut self.app_state, self.view.id()); - self.view.style_main(&mut cx); + cx.style_view(&mut self.view); } fn layout(&mut self) -> Duration { let mut cx = LayoutCx::new(&mut self.app_state); - cx.app_state_mut().root = Some(self.view.layout_main(&mut cx)); + cx.app_state_mut().root = Some(cx.layout_view(&mut self.view)); let start = Instant::now(); cx.app_state_mut().compute_layout(); let taffy_duration = Instant::now().saturating_duration_since(start); cx.clear(); - self.view.compute_layout_main(&mut cx); + cx.compute_view_layout(&mut self.view); if self.app_state.capture.is_none() && !self.app_state.animated.is_empty() { let animated = self.app_state.animated.clone(); @@ -525,7 +533,7 @@ impl WindowHandle { 0.0, ); } - self.view.paint_main(&mut cx); + cx.paint_view(&mut self.view); if let Some(window) = self.window.as_ref() { if cx.app_state.capture.is_none() { window.pre_present_notify(); @@ -746,7 +754,7 @@ impl WindowHandle { UpdateMessage::State { id, state } => { let id_path = ID_PATHS.with(|paths| paths.borrow().get(&id).cloned()); if let Some(id_path) = id_path { - flags |= self.view.update_main(&mut cx, id_path.dispatch(), state); + flags |= cx.update_view(&mut self.view, id_path.dispatch(), state); } } UpdateMessage::BaseStyle { id, style } => { @@ -941,7 +949,7 @@ impl WindowHandle { for (id, state) in msgs { let id_path = ID_PATHS.with(|paths| paths.borrow().get(&id).cloned()); if let Some(id_path) = id_path { - flags |= self.view.update_main(&mut cx, id_path.dispatch(), state); + flags |= cx.update_view(&mut self.view, id_path.dispatch(), state); } }