@@ -45,7 +45,7 @@ use crate::{
executor::{self, Task},
keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
platform::{
- self, Appearance, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
+ self, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions,
},
util::post_inc,
@@ -872,60 +872,6 @@ impl AppContext {
self.active_labeled_tasks.values().cloned()
}
- pub fn render_view(&mut self, params: RenderParams) -> Result<Box<dyn AnyRootElement>> {
- todo!()
- // let window_id = params.window_id;
- // let view_id = params.view_id;
- // let mut view = self
- // .views
- // .remove(&(window_id, view_id))
- // .ok_or_else(|| anyhow!("view not found"))?;
- // let element = view.render(params, self);
- // self.views.insert((window_id, view_id), view);
- // Ok(element)
- }
-
- pub fn render_views(
- &mut self,
- window_id: usize,
- titlebar_height: f32,
- appearance: Appearance,
- ) -> HashMap<usize, Box<dyn AnyRootElement>> {
- todo!()
- // self.start_frame();
- // #[allow(clippy::needless_collect)]
- // let view_ids = self
- // .views
- // .keys()
- // .filter_map(|(win_id, view_id)| {
- // if *win_id == window_id {
- // Some(*view_id)
- // } else {
- // None
- // }
- // })
- // .collect::<Vec<_>>();
-
- // view_ids
- // .into_iter()
- // .map(|view_id| {
- // (
- // view_id,
- // self.render_view(RenderParams {
- // window_id,
- // view_id,
- // titlebar_height,
- // hovered_region_ids: Default::default(),
- // clicked_region_ids: None,
- // refreshing: false,
- // appearance,
- // })
- // .unwrap(),
- // )
- // })
- // .collect()
- }
-
pub(crate) fn start_frame(&mut self) {
self.frame_count += 1;
}
@@ -2488,19 +2434,8 @@ impl UpdateView for AppContext {
where
T: View,
{
- self.update_window(handle.window_id, |cx| {
- cx.update_any_view(handle.view_id, |view, cx| {
- let mut cx = ViewContext::mutable(cx, handle.view_id);
- update(
- view.as_any_mut()
- .downcast_mut()
- .expect("downcast is type safe"),
- &mut cx,
- )
- })
- .unwrap() // TODO: Are these unwraps safe?
- })
- .unwrap()
+ self.update_window(handle.window_id, |cx| cx.update_view(handle, update))
+ .unwrap() // TODO: Is this unwrap safe?
}
}
@@ -3941,16 +3876,6 @@ impl<'a, T> DerefMut for Reference<'a, T> {
}
}
-pub struct RenderParams {
- pub window_id: usize,
- pub view_id: usize,
- pub titlebar_height: f32,
- pub hovered_region_ids: HashSet<MouseRegionId>,
- pub clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
- pub refreshing: bool,
- pub appearance: Appearance,
-}
-
#[derive(Debug, Clone, Default)]
pub struct MouseState {
pub(crate) hovered: bool,
@@ -14,11 +14,11 @@ use crate::{
text_layout::TextLayoutCache,
util::post_inc,
AnyView, AnyViewHandle, AnyWeakViewHandle, AppContext, Drawable, Entity, ModelContext,
- ModelHandle, MouseRegion, MouseRegionId, ParentId, ReadView, RenderParams, SceneBuilder,
- UpdateModel, UpdateView, UpgradeViewHandle, View, ViewContext, ViewHandle, WeakViewHandle,
+ ModelHandle, MouseRegion, MouseRegionId, ParentId, ReadView, SceneBuilder, UpdateModel,
+ UpdateView, UpgradeViewHandle, View, ViewContext, ViewHandle, WeakViewHandle,
WindowInvalidation,
};
-use anyhow::bail;
+use anyhow::{anyhow, bail, Result};
use collections::{HashMap, HashSet};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use postage::oneshot;
@@ -74,7 +74,7 @@ impl Window {
invalidation: None,
is_fullscreen: false,
platform_window,
- rendered_views: cx.render_views(window_id, titlebar_height, appearance),
+ rendered_views: Default::default(),
cursor_regions: Default::default(),
mouse_regions: Default::default(),
text_layout_cache: TextLayoutCache::new(cx.font_system.clone()),
@@ -91,6 +91,9 @@ impl Window {
let root_view = window_context
.build_and_insert_view(ParentId::Root, |cx| Some(build_view(cx)))
.unwrap();
+ if let Some(mut invalidation) = window_context.window.invalidation.take() {
+ window_context.invalidate(&mut invalidation, appearance);
+ }
window.focused_view_id = Some(root_view.id());
window.root_view = Some(root_view.into_any());
window
@@ -150,7 +153,16 @@ impl UpdateView for WindowContext<'_, '_> {
where
T: View,
{
- self.app_context.update_view(handle, update)
+ self.update_any_view(handle.view_id, |view, cx| {
+ let mut cx = ViewContext::mutable(cx, handle.view_id);
+ update(
+ view.as_any_mut()
+ .downcast_mut()
+ .expect("downcast is type safe"),
+ &mut cx,
+ )
+ })
+ .unwrap() // TODO: Is this unwrap safe?
}
}
@@ -678,7 +690,6 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
self.window.rendered_views.remove(view_id);
}
for view_id in &invalidation.updated {
- let window_id = self.window_id;
let titlebar_height = self.window.titlebar_height;
let hovered_region_ids = self.window.hovered_region_ids.clone();
let clicked_region_ids = self
@@ -688,7 +699,6 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
let element = self
.render_view(RenderParams {
- window_id,
view_id: *view_id,
titlebar_height,
hovered_region_ids,
@@ -701,39 +711,16 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
}
}
- pub fn refresh(&mut self, invalidation: &mut WindowInvalidation, appearance: Appearance) {
- self.invalidate(invalidation, appearance);
-
- let view_ids = self
- .window
- .rendered_views
- .keys()
- .copied()
- .collect::<Vec<_>>();
-
- for view_id in view_ids {
- if !invalidation.updated.contains(&view_id) {
- let window_id = self.window_id;
- let titlebar_height = self.window.titlebar_height;
- let hovered_region_ids = self.window.hovered_region_ids.clone();
- let clicked_region_ids = self
- .window
- .clicked_button
- .map(|button| (self.window.clicked_region_ids.clone(), button));
- let element = self
- .render_view(RenderParams {
- window_id,
- view_id,
- titlebar_height,
- hovered_region_ids,
- clicked_region_ids,
- refreshing: true,
- appearance,
- })
- .unwrap();
- self.window.rendered_views.insert(view_id, element);
- }
- }
+ pub fn render_view(&mut self, params: RenderParams) -> Result<Box<dyn AnyRootElement>> {
+ let window_id = self.window_id;
+ let view_id = params.view_id;
+ let mut view = self
+ .views
+ .remove(&(window_id, view_id))
+ .ok_or_else(|| anyhow!("view not found"))?;
+ let element = view.render(self, view_id);
+ self.views.insert((window_id, view_id), view);
+ Ok(element)
}
pub fn build_scene(&mut self) -> Scene {
@@ -854,32 +841,6 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
self.window.platform_window.prompt(level, msg, answers)
}
- fn add_view<T, F>(&mut self, parent: &AnyViewHandle, build_view: F) -> ViewHandle<T>
- where
- T: View,
- F: FnOnce(&mut ViewContext<T>) -> T,
- {
- if parent.window_id == self.window_id {
- self.build_and_insert_view(ParentId::View(parent.view_id), |cx| Some(build_view(cx)))
- .unwrap()
- } else {
- self.app_context.add_view(parent, build_view)
- }
- }
-
- fn add_option_view<T, F>(
- &mut self,
- parent_handle: impl Into<AnyViewHandle>,
- build_view: F,
- ) -> Option<ViewHandle<T>>
- where
- T: View,
- F: FnOnce(&mut ViewContext<T>) -> Option<T>,
- {
- let parent_handle = parent_handle.into();
- self.build_and_insert_view(ParentId::View(parent_handle.view_id), build_view)
- }
-
pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> ViewHandle<V>
where
V: View,
@@ -923,6 +884,15 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
}
}
+pub struct RenderParams {
+ pub view_id: usize,
+ pub titlebar_height: f32,
+ pub hovered_region_ids: HashSet<MouseRegionId>,
+ pub clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
+ pub refreshing: bool,
+ pub appearance: Appearance,
+}
+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Axis {
#[default]
@@ -648,267 +648,265 @@ mod tests {
use super::*;
use crate::{elements::Empty, geometry::vector::vec2f, Entity};
use rand::prelude::*;
+ use std::env;
#[crate::test(self)]
fn test_layout(cx: &mut crate::AppContext) {
- todo!()
- // let (_, view) = cx.add_window(Default::default(), |_| TestView);
- // let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
-
- // let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)]));
-
- // let state = view.update(cx, |_, cx| {
- // ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, cx, {
- // let elements = elements.clone();
- // move |_, ix, _| {
- // let (id, height) = elements.borrow()[ix];
- // TestElement::new(id, height).boxed()
- // }
- // })
- // });
-
- // let mut list = List::new(state.clone());
- // let (size, _) = list.layout(
- // constraint,
- // &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
- // );
- // assert_eq!(size, vec2f(100., 40.));
- // assert_eq!(
- // state.0.borrow().items.summary().clone(),
- // ListItemSummary {
- // count: 3,
- // rendered_count: 3,
- // unrendered_count: 0,
- // height: 150.
- // }
- // );
-
- // state.0.borrow_mut().scroll(
- // &ListOffset {
- // item_ix: 0,
- // offset_in_item: 0.,
- // },
- // 40.,
- // vec2f(0., -54.),
- // true,
- // &mut presenter.build_event_context(&mut Default::default(), cx),
- // );
- // let (_, logical_scroll_top) = list.layout(
- // constraint,
- // &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
- // );
- // assert_eq!(
- // logical_scroll_top,
- // ListOffset {
- // item_ix: 2,
- // offset_in_item: 4.
- // }
- // );
- // assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 54.);
-
- // elements.borrow_mut().splice(1..2, vec![(3, 40.), (4, 50.)]);
- // elements.borrow_mut().push((5, 60.));
- // state.splice(1..2, 2);
- // state.splice(4..4, 1);
- // assert_eq!(
- // state.0.borrow().items.summary().clone(),
- // ListItemSummary {
- // count: 5,
- // rendered_count: 2,
- // unrendered_count: 3,
- // height: 120.
- // }
- // );
-
- // let (size, logical_scroll_top) = list.layout(
- // constraint,
- // &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
- // );
- // assert_eq!(size, vec2f(100., 40.));
- // assert_eq!(
- // state.0.borrow().items.summary().clone(),
- // ListItemSummary {
- // count: 5,
- // rendered_count: 5,
- // unrendered_count: 0,
- // height: 270.
- // }
- // );
- // assert_eq!(
- // logical_scroll_top,
- // ListOffset {
- // item_ix: 3,
- // offset_in_item: 4.
- // }
- // );
- // assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 114.);
- }
-
- #[crate::test(self, iterations = 10, seed = 0)]
+ cx.add_window(Default::default(), |cx| {
+ let mut view = TestView;
+ let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
+ let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)]));
+ let state = ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, {
+ let elements = elements.clone();
+ move |_, ix, _| {
+ let (id, height) = elements.borrow()[ix];
+ TestElement::new(id, height).boxed()
+ }
+ });
+
+ let mut list = List::new(state.clone());
+ let (size, _) = list.layout(constraint, &mut view, cx);
+ assert_eq!(size, vec2f(100., 40.));
+ assert_eq!(
+ state.0.borrow().items.summary().clone(),
+ ListItemSummary {
+ count: 3,
+ rendered_count: 3,
+ unrendered_count: 0,
+ height: 150.
+ }
+ );
+
+ state.0.borrow_mut().scroll(
+ &ListOffset {
+ item_ix: 0,
+ offset_in_item: 0.,
+ },
+ 40.,
+ vec2f(0., -54.),
+ true,
+ &mut view,
+ cx,
+ );
+
+ let (_, logical_scroll_top) = list.layout(constraint, &mut view, cx);
+ assert_eq!(
+ logical_scroll_top,
+ ListOffset {
+ item_ix: 2,
+ offset_in_item: 4.
+ }
+ );
+ assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 54.);
+
+ elements.borrow_mut().splice(1..2, vec![(3, 40.), (4, 50.)]);
+ elements.borrow_mut().push((5, 60.));
+ state.splice(1..2, 2);
+ state.splice(4..4, 1);
+ assert_eq!(
+ state.0.borrow().items.summary().clone(),
+ ListItemSummary {
+ count: 5,
+ rendered_count: 2,
+ unrendered_count: 3,
+ height: 120.
+ }
+ );
+
+ let (size, logical_scroll_top) = list.layout(constraint, &mut view, cx);
+ assert_eq!(size, vec2f(100., 40.));
+ assert_eq!(
+ state.0.borrow().items.summary().clone(),
+ ListItemSummary {
+ count: 5,
+ rendered_count: 5,
+ unrendered_count: 0,
+ height: 270.
+ }
+ );
+ assert_eq!(
+ logical_scroll_top,
+ ListOffset {
+ item_ix: 3,
+ offset_in_item: 4.
+ }
+ );
+ assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 114.);
+
+ view
+ });
+ }
+
+ #[crate::test(self, iterations = 10)]
fn test_random(cx: &mut crate::AppContext, mut rng: StdRng) {
- todo!()
- // let operations = env::var("OPERATIONS")
- // .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
- // .unwrap_or(10);
-
- // let (window_id, view) = cx.add_window(Default::default(), |_| TestView);
- // let mut next_id = 0;
- // let elements = Rc::new(RefCell::new(
- // (0..rng.gen_range(0..=20))
- // .map(|_| {
- // let id = next_id;
- // next_id += 1;
- // (id, rng.gen_range(0..=200) as f32 / 2.0)
- // })
- // .collect::<Vec<_>>(),
- // ));
- // let orientation = *[Orientation::Top, Orientation::Bottom]
- // .choose(&mut rng)
- // .unwrap();
- // let overdraw = rng.gen_range(1..=100) as f32;
-
- // let state = view.update(cx, |_, cx| {
- // ListState::new(elements.borrow().len(), orientation, overdraw, cx, {
- // let elements = elements.clone();
- // move |_, ix, _| {
- // let (id, height) = elements.borrow()[ix];
- // TestElement::new(id, height).boxed()
- // }
- // })
- // });
-
- // let mut width = rng.gen_range(0..=2000) as f32 / 2.;
- // let mut height = rng.gen_range(0..=2000) as f32 / 2.;
- // log::info!("orientation: {:?}", orientation);
- // log::info!("overdraw: {}", overdraw);
- // log::info!("elements: {:?}", elements.borrow());
- // log::info!("size: ({:?}, {:?})", width, height);
- // log::info!("==================");
-
- // let mut last_logical_scroll_top = None;
- // for _ in 0..operations {
- // match rng.gen_range(0..=100) {
- // 0..=29 if last_logical_scroll_top.is_some() => {
- // let delta = vec2f(0., rng.gen_range(-overdraw..=overdraw));
- // log::info!(
- // "Scrolling by {:?}, previous scroll top: {:?}",
- // delta,
- // last_logical_scroll_top.unwrap()
- // );
- // state.0.borrow_mut().scroll(
- // last_logical_scroll_top.as_ref().unwrap(),
- // height,
- // delta,
- // true,
- // &mut presenter.build_event_context(&mut Default::default(), cx),
- // );
- // }
- // 30..=34 => {
- // width = rng.gen_range(0..=2000) as f32 / 2.;
- // log::info!("changing width: {:?}", width);
- // }
- // 35..=54 => {
- // height = rng.gen_range(0..=1000) as f32 / 2.;
- // log::info!("changing height: {:?}", height);
- // }
- // _ => {
- // let mut elements = elements.borrow_mut();
- // let end_ix = rng.gen_range(0..=elements.len());
- // let start_ix = rng.gen_range(0..=end_ix);
- // let new_elements = (0..rng.gen_range(0..10))
- // .map(|_| {
- // let id = next_id;
- // next_id += 1;
- // (id, rng.gen_range(0..=200) as f32 / 2.)
- // })
- // .collect::<Vec<_>>();
- // log::info!("splice({:?}, {:?})", start_ix..end_ix, new_elements);
- // state.splice(start_ix..end_ix, new_elements.len());
- // elements.splice(start_ix..end_ix, new_elements);
- // for (ix, item) in state.0.borrow().items.cursor::<()>().enumerate() {
- // if let ListItem::Rendered(element) = item {
- // let (expected_id, _) = elements[ix];
- // element.with_metadata(|metadata: Option<&usize>| {
- // assert_eq!(*metadata.unwrap(), expected_id);
- // });
- // }
- // }
- // }
- // }
-
- // let mut list = List::new(state.clone());
- // let window_size = vec2f(width, height);
- // let (size, logical_scroll_top) = list.layout(
- // SizeConstraint::new(vec2f(0., 0.), window_size),
- // &mut presenter.build_layout_context(window_size, false, cx),
- // );
- // assert_eq!(size, window_size);
- // last_logical_scroll_top = Some(logical_scroll_top);
-
- // let state = state.0.borrow();
- // log::info!("items {:?}", state.items.items(&()));
-
- // let scroll_top = state.scroll_top(&logical_scroll_top);
- // let rendered_top = (scroll_top - overdraw).max(0.);
- // let rendered_bottom = scroll_top + height + overdraw;
- // let mut item_top = 0.;
-
- // log::info!(
- // "rendered top {:?}, rendered bottom {:?}, scroll top {:?}",
- // rendered_top,
- // rendered_bottom,
- // scroll_top,
- // );
-
- // let mut first_rendered_element_top = None;
- // let mut last_rendered_element_bottom = None;
- // assert_eq!(state.items.summary().count, elements.borrow().len());
- // for (ix, item) in state.items.cursor::<()>().enumerate() {
- // match item {
- // ListItem::Unrendered => {
- // let item_bottom = item_top;
- // assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
- // item_top = item_bottom;
- // }
- // ListItem::Removed(height) => {
- // let (id, expected_height) = elements.borrow()[ix];
- // assert_eq!(
- // *height, expected_height,
- // "element {} height didn't match",
- // id
- // );
- // let item_bottom = item_top + height;
- // assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
- // item_top = item_bottom;
- // }
- // ListItem::Rendered(element) => {
- // let (expected_id, expected_height) = elements.borrow()[ix];
- // element.with_metadata(|metadata: Option<&usize>| {
- // assert_eq!(*metadata.unwrap(), expected_id);
- // });
- // assert_eq!(element.size().y(), expected_height);
- // let item_bottom = item_top + element.size().y();
- // first_rendered_element_top.get_or_insert(item_top);
- // last_rendered_element_bottom = Some(item_bottom);
- // assert!(item_bottom > rendered_top || item_top < rendered_bottom);
- // item_top = item_bottom;
- // }
- // }
- // }
-
- // match orientation {
- // Orientation::Top => {
- // if let Some(first_rendered_element_top) = first_rendered_element_top {
- // assert!(first_rendered_element_top <= scroll_top);
- // }
- // }
- // Orientation::Bottom => {
- // if let Some(last_rendered_element_bottom) = last_rendered_element_bottom {
- // assert!(last_rendered_element_bottom >= scroll_top + height);
- // }
- // }
- // }
- // }
+ let operations = env::var("OPERATIONS")
+ .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+ .unwrap_or(10);
+
+ cx.add_window(Default::default(), |cx| {
+ let mut view = TestView;
+
+ let mut next_id = 0;
+ let elements = Rc::new(RefCell::new(
+ (0..rng.gen_range(0..=20))
+ .map(|_| {
+ let id = next_id;
+ next_id += 1;
+ (id, rng.gen_range(0..=200) as f32 / 2.0)
+ })
+ .collect::<Vec<_>>(),
+ ));
+ let orientation = *[Orientation::Top, Orientation::Bottom]
+ .choose(&mut rng)
+ .unwrap();
+ let overdraw = rng.gen_range(1..=100) as f32;
+
+ let state = ListState::new(elements.borrow().len(), orientation, overdraw, {
+ let elements = elements.clone();
+ move |_, ix, _| {
+ let (id, height) = elements.borrow()[ix];
+ TestElement::new(id, height).boxed()
+ }
+ });
+
+ let mut width = rng.gen_range(0..=2000) as f32 / 2.;
+ let mut height = rng.gen_range(0..=2000) as f32 / 2.;
+ log::info!("orientation: {:?}", orientation);
+ log::info!("overdraw: {}", overdraw);
+ log::info!("elements: {:?}", elements.borrow());
+ log::info!("size: ({:?}, {:?})", width, height);
+ log::info!("==================");
+
+ let mut last_logical_scroll_top = None;
+ for _ in 0..operations {
+ match rng.gen_range(0..=100) {
+ 0..=29 if last_logical_scroll_top.is_some() => {
+ let delta = vec2f(0., rng.gen_range(-overdraw..=overdraw));
+ log::info!(
+ "Scrolling by {:?}, previous scroll top: {:?}",
+ delta,
+ last_logical_scroll_top.unwrap()
+ );
+ state.0.borrow_mut().scroll(
+ last_logical_scroll_top.as_ref().unwrap(),
+ height,
+ delta,
+ true,
+ &mut view,
+ cx,
+ );
+ }
+ 30..=34 => {
+ width = rng.gen_range(0..=2000) as f32 / 2.;
+ log::info!("changing width: {:?}", width);
+ }
+ 35..=54 => {
+ height = rng.gen_range(0..=1000) as f32 / 2.;
+ log::info!("changing height: {:?}", height);
+ }
+ _ => {
+ let mut elements = elements.borrow_mut();
+ let end_ix = rng.gen_range(0..=elements.len());
+ let start_ix = rng.gen_range(0..=end_ix);
+ let new_elements = (0..rng.gen_range(0..10))
+ .map(|_| {
+ let id = next_id;
+ next_id += 1;
+ (id, rng.gen_range(0..=200) as f32 / 2.)
+ })
+ .collect::<Vec<_>>();
+ log::info!("splice({:?}, {:?})", start_ix..end_ix, new_elements);
+ state.splice(start_ix..end_ix, new_elements.len());
+ elements.splice(start_ix..end_ix, new_elements);
+ for (ix, item) in state.0.borrow().items.cursor::<()>().enumerate() {
+ if let ListItem::Rendered(element) = item {
+ let (expected_id, _) = elements[ix];
+ element.borrow().with_metadata(|metadata: Option<&usize>| {
+ assert_eq!(*metadata.unwrap(), expected_id);
+ });
+ }
+ }
+ }
+ }
+
+ let mut list = List::new(state.clone());
+ let window_size = vec2f(width, height);
+ let (size, logical_scroll_top) = list.layout(
+ SizeConstraint::new(vec2f(0., 0.), window_size),
+ &mut view,
+ cx,
+ );
+ assert_eq!(size, window_size);
+ last_logical_scroll_top = Some(logical_scroll_top);
+
+ let state = state.0.borrow();
+ log::info!("items {:?}", state.items.items(&()));
+
+ let scroll_top = state.scroll_top(&logical_scroll_top);
+ let rendered_top = (scroll_top - overdraw).max(0.);
+ let rendered_bottom = scroll_top + height + overdraw;
+ let mut item_top = 0.;
+
+ log::info!(
+ "rendered top {:?}, rendered bottom {:?}, scroll top {:?}",
+ rendered_top,
+ rendered_bottom,
+ scroll_top,
+ );
+
+ let mut first_rendered_element_top = None;
+ let mut last_rendered_element_bottom = None;
+ assert_eq!(state.items.summary().count, elements.borrow().len());
+ for (ix, item) in state.items.cursor::<()>().enumerate() {
+ match item {
+ ListItem::Unrendered => {
+ let item_bottom = item_top;
+ assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
+ item_top = item_bottom;
+ }
+ ListItem::Removed(height) => {
+ let (id, expected_height) = elements.borrow()[ix];
+ assert_eq!(
+ *height, expected_height,
+ "element {} height didn't match",
+ id
+ );
+ let item_bottom = item_top + height;
+ assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
+ item_top = item_bottom;
+ }
+ ListItem::Rendered(element) => {
+ let (expected_id, expected_height) = elements.borrow()[ix];
+ let element = element.borrow();
+ element.with_metadata(|metadata: Option<&usize>| {
+ assert_eq!(*metadata.unwrap(), expected_id);
+ });
+ assert_eq!(element.size().y(), expected_height);
+ let item_bottom = item_top + element.size().y();
+ first_rendered_element_top.get_or_insert(item_top);
+ last_rendered_element_bottom = Some(item_bottom);
+ assert!(item_bottom > rendered_top || item_top < rendered_bottom);
+ item_top = item_bottom;
+ }
+ }
+ }
+
+ match orientation {
+ Orientation::Top => {
+ if let Some(first_rendered_element_top) = first_rendered_element_top {
+ assert!(first_rendered_element_top <= scroll_top);
+ }
+ }
+ Orientation::Bottom => {
+ if let Some(last_rendered_element_bottom) = last_rendered_element_bottom {
+ assert!(last_rendered_element_bottom >= scroll_top + height);
+ }
+ }
+ }
+ }
+
+ view
+ });
}
struct TestView;