Skip to content

Commit

Permalink
Specifiy a viewport for the window and add ComputeLayoutCx (#177)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc authored Nov 15, 2023
1 parent ec99b9c commit 7f9aff0
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 127 deletions.
203 changes: 97 additions & 106 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1303,32 +1303,31 @@ impl<'a> StyleCx<'a> {
}
}

/// Holds current layout state for given position in the tree.
/// You'll use this in the `View::layout` implementation to call `layout_node` on children and to access any font
pub struct LayoutCx<'a> {
pub struct ComputeLayoutCx<'a> {
pub(crate) app_state: &'a mut AppState,
pub(crate) viewport: Option<Rect>,
pub(crate) viewport: Rect,
pub(crate) window_origin: Point,
pub(crate) saved_viewports: Vec<Option<Rect>>,
pub(crate) saved_viewports: Vec<Rect>,
pub(crate) saved_window_origins: Vec<Point>,
}

impl<'a> LayoutCx<'a> {
pub(crate) fn new(app_state: &'a mut AppState) -> Self {
impl<'a> ComputeLayoutCx<'a> {
pub(crate) fn new(app_state: &'a mut AppState, viewport: Rect) -> Self {
Self {
app_state,
viewport: None,
viewport,
window_origin: Point::ZERO,
saved_viewports: Vec::new(),
saved_window_origins: Vec::new(),
}
}

pub(crate) fn clear(&mut self) {
self.viewport = None;
self.window_origin = Point::ZERO;
self.saved_viewports.clear();
self.saved_window_origins.clear();
pub fn app_state_mut(&mut self) -> &mut AppState {
self.app_state
}

pub fn app_state(&self) -> &AppState {
self.app_state
}

pub fn save(&mut self) {
Expand All @@ -1341,79 +1340,31 @@ impl<'a> LayoutCx<'a> {
self.window_origin = self.saved_window_origins.pop().unwrap_or_default();
}

pub fn app_state_mut(&mut self) -> &mut AppState {
self.app_state
}

pub fn app_state(&self) -> &AppState {
self.app_state
}

pub fn current_viewport(&self) -> Option<Rect> {
pub fn current_viewport(&self) -> Rect {
self.viewport
}

pub fn get_layout(&self, id: Id) -> Option<Layout> {
self.app_state.get_layout(id)
}

pub fn get_computed_style(&mut self, id: Id) -> &Style {
self.app_state.get_computed_style(id)
}

pub fn set_style(&mut self, node: Node, style: taffy::style::Style) {
let _ = self.app_state.taffy.set_style(node, style);
}

pub fn layout(&self, node: Node) -> Option<Layout> {
self.app_state.taffy.layout(node).ok().copied()
}

pub fn new_node(&mut self) -> Node {
pub(crate) fn get_resize_listener(&mut self, id: Id) -> Option<&mut ResizeListener> {
self.app_state
.taffy
.new_leaf(taffy::style::Style::DEFAULT)
.unwrap()
}

/// Responsible for invoking the recalculation of style and thus the layout and
/// creating or updating the layout of child nodes within the closure.
///
/// You should ensure that all children are laid out within the closure and/or whatever
/// other work you need to do to ensure that the layout for the returned nodes is correct.
pub fn layout_node(
&mut self,
id: Id,
has_children: bool,
mut children: impl FnMut(&mut LayoutCx) -> Vec<Node>,
) -> Node {
let view_state = self.app_state.view_state(id);
let node = view_state.node;
if !view_state.requested_changes.contains(ChangeFlags::LAYOUT) {
return node;
}
view_state.requested_changes.remove(ChangeFlags::LAYOUT);
let style = view_state.combined_style.to_taffy_style();
let _ = self.app_state.taffy.set_style(node, style);

if has_children {
let nodes = children(self);
let _ = self.app_state.taffy.set_children(node, &nodes);
let view = self.app_state.view_state(id);
view.children_nodes = nodes;
}

node
.view_states
.get_mut(&id)
.and_then(|s| s.resize_listener.as_mut())
}

/// 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
pub(crate) fn get_move_listener(&mut self, id: Id) -> Option<&mut MoveListener> {
self.app_state
.view_states
.get_mut(&id)
.and_then(|s| s.move_listener.as_mut())
}

/// 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:
Expand All @@ -1438,36 +1389,20 @@ impl<'a> LayoutCx<'a> {
.view_states
.get(&id)
.and_then(|view| view.viewport);
let this_viewport_origin = this_viewport.unwrap_or_default().origin().to_vec2();
let size = Size::new(layout.size.width as f64, layout.size.height 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,
) + this_viewport.unwrap_or_default().origin().to_vec2(),
)
});
match (parent_viewport, this_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 parent_viewport = self.viewport.with_origin(
Point::new(
self.viewport.x0 - layout.location.x as f64,
self.viewport.y0 - layout.location.y as f64,
) + this_viewport_origin,
);
self.viewport = parent_viewport.intersect(size.to_rect());
if let Some(this_viewport) = this_viewport {
self.viewport = self.viewport.intersect(this_viewport);
}

let window_origin = origin + self.window_origin.to_vec2()
- this_viewport.unwrap_or_default().origin().to_vec2();
let window_origin = origin + self.window_origin.to_vec2() - this_viewport_origin;
self.window_origin = window_origin;

if let Some(resize) = self.get_resize_listener(id) {
Expand Down Expand Up @@ -1499,19 +1434,75 @@ impl<'a> LayoutCx<'a> {

Some(layout_rect)
}
}

pub(crate) fn get_resize_listener(&mut self, id: Id) -> Option<&mut ResizeListener> {
/// Holds current layout state for given position in the tree.
/// You'll use this in the `View::layout` implementation to call `layout_node` on children and to access any font
pub struct LayoutCx<'a> {
pub(crate) app_state: &'a mut AppState,
}

impl<'a> LayoutCx<'a> {
pub(crate) fn new(app_state: &'a mut AppState) -> Self {
Self { app_state }
}

pub fn app_state_mut(&mut self) -> &mut AppState {
self.app_state
.view_states
.get_mut(&id)
.and_then(|s| s.resize_listener.as_mut())
}

pub(crate) fn get_move_listener(&mut self, id: Id) -> Option<&mut MoveListener> {
pub fn app_state(&self) -> &AppState {
self.app_state
.view_states
.get_mut(&id)
.and_then(|s| s.move_listener.as_mut())
}

pub fn get_computed_style(&mut self, id: Id) -> &Style {
self.app_state.get_computed_style(id)
}

pub fn set_style(&mut self, node: Node, style: taffy::style::Style) {
let _ = self.app_state.taffy.set_style(node, style);
}

pub fn new_node(&mut self) -> Node {
self.app_state
.taffy
.new_leaf(taffy::style::Style::DEFAULT)
.unwrap()
}

/// Responsible for invoking the recalculation of style and thus the layout and
/// creating or updating the layout of child nodes within the closure.
///
/// You should ensure that all children are laid out within the closure and/or whatever
/// other work you need to do to ensure that the layout for the returned nodes is correct.
pub fn layout_node(
&mut self,
id: Id,
has_children: bool,
mut children: impl FnMut(&mut LayoutCx) -> Vec<Node>,
) -> Node {
let view_state = self.app_state.view_state(id);
let node = view_state.node;
if !view_state.requested_changes.contains(ChangeFlags::LAYOUT) {
return node;
}
view_state.requested_changes.remove(ChangeFlags::LAYOUT);
let style = view_state.combined_style.to_taffy_style();
let _ = self.app_state.taffy.set_style(node, style);

if has_children {
let nodes = children(self);
let _ = self.app_state.taffy.set_children(node, &nodes);
let view = self.app_state.view_state(id);
view.children_nodes = nodes;
}

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 {
view.layout(self)
}
}

Expand Down
13 changes: 9 additions & 4 deletions src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ use std::any::Any;
use taffy::prelude::Node;

use crate::{
context::{AppState, EventCx, LayoutCx, PaintCx, StyleCx, UpdateCx, ViewStyleProps},
context::{
AppState, ComputeLayoutCx, EventCx, LayoutCx, PaintCx, StyleCx, UpdateCx, ViewStyleProps,
},
event::Event,
id::{Id, ID_PATHS},
style::{BoxShadowProp, Style, StyleClassRef},
Expand Down Expand Up @@ -202,7 +204,7 @@ pub trait View {
///
/// If the layout changes needs other passes to run you're expected to call
/// `cx.app_state_mut().request_changes`.
fn compute_layout(&mut self, cx: &mut LayoutCx) -> Option<Rect> {
fn compute_layout(&mut self, cx: &mut ComputeLayoutCx) -> Option<Rect> {
default_compute_layout(self, cx)
}

Expand Down Expand Up @@ -277,7 +279,10 @@ pub(crate) fn update_data(id: Id, root: &mut dyn View, f: impl FnOnce(&mut ViewD
}

/// Computes the layout of the view's children, if any.
pub fn default_compute_layout<V: View + ?Sized>(view: &mut V, cx: &mut LayoutCx) -> Option<Rect> {
pub fn default_compute_layout<V: View + ?Sized>(
view: &mut V,
cx: &mut ComputeLayoutCx,
) -> Option<Rect> {
let mut layout_rect: Option<Rect> = None;
view.for_each_child_mut(&mut |child| {
let child_layout = cx.compute_view_layout(child);
Expand Down Expand Up @@ -685,7 +690,7 @@ impl View for Box<dyn View> {
(**self).layout(cx)
}

fn compute_layout(&mut self, cx: &mut LayoutCx) -> Option<Rect> {
fn compute_layout(&mut self, cx: &mut ComputeLayoutCx) -> Option<Rect> {
(**self).compute_layout(cx)
}

Expand Down
2 changes: 1 addition & 1 deletion src/views/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl View for Label {
})
}

fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option<Rect> {
fn compute_layout(&mut self, cx: &mut crate::context::ComputeLayoutCx) -> Option<Rect> {
if self.label.is_empty() {
return None;
}
Expand Down
2 changes: 1 addition & 1 deletion src/views/rich_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl View for RichText {
})
}

fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option<Rect> {
fn compute_layout(&mut self, cx: &mut crate::context::ComputeLayoutCx) -> Option<Rect> {
let layout = cx.get_layout(self.id()).unwrap();
let style = cx.app_state_mut().get_builtin_style(self.id());
let padding_left = match style.padding_left() {
Expand Down
4 changes: 2 additions & 2 deletions src/views/scroll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use peniko::Color;
use taffy::{prelude::Node, style::Position};

use crate::{
context::{AppState, LayoutCx, PaintCx},
context::{AppState, ComputeLayoutCx, PaintCx},
event::Event,
id::Id,
prop, prop_extracter,
Expand Down Expand Up @@ -714,7 +714,7 @@ impl View for Scroll {
})
}

fn compute_layout(&mut self, cx: &mut LayoutCx) -> Option<Rect> {
fn compute_layout(&mut self, cx: &mut ComputeLayoutCx) -> Option<Rect> {
self.update_size(cx.app_state_mut());
self.clamp_child_viewport(cx.app_state_mut(), self.child_viewport);
self.computed_child_viewport = self.child_viewport;
Expand Down
2 changes: 1 addition & 1 deletion src/views/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ impl View for TextInput {
})
}

fn compute_layout(&mut self, _cx: &mut crate::context::LayoutCx) -> Option<Rect> {
fn compute_layout(&mut self, _cx: &mut crate::context::ComputeLayoutCx) -> Option<Rect> {
self.update_text_layout();
None
}
Expand Down
6 changes: 3 additions & 3 deletions src/views/virtual_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use smallvec::SmallVec;
use taffy::{prelude::Node, style::Dimension};

use crate::{
context::LayoutCx,
context::ComputeLayoutCx,
id::Id,
view::{self, View, ViewData},
};
Expand Down Expand Up @@ -331,8 +331,8 @@ impl<V: View + 'static, T> View for VirtualList<V, T> {
})
}

fn compute_layout(&mut self, cx: &mut LayoutCx) -> Option<Rect> {
let viewport = cx.viewport.unwrap_or_default();
fn compute_layout(&mut self, cx: &mut ComputeLayoutCx<'_>) -> Option<Rect> {
let viewport = cx.current_viewport();
if self.viewport != viewport {
let layout = cx.app_state().get_layout(self.id()).unwrap();
let _size = Size::new(layout.size.width as f64, layout.size.height as f64);
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl View for Slider {
}
}

fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option<kurbo::Rect> {
fn compute_layout(&mut self, cx: &mut crate::context::ComputeLayoutCx) -> Option<kurbo::Rect> {
let layout = cx.get_layout(self.id()).unwrap();

self.size = layout.size;
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/toggle_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl View for ToggleButton {
EventPropagation::Continue
}

fn compute_layout(&mut self, cx: &mut crate::context::LayoutCx) -> Option<kurbo::Rect> {
fn compute_layout(&mut self, cx: &mut crate::context::ComputeLayoutCx) -> Option<kurbo::Rect> {
let layout = cx.get_layout(self.id()).unwrap();
let size = layout.size;
self.width = size.width;
Expand Down
Loading

0 comments on commit 7f9aff0

Please sign in to comment.