From 1ffaefd729bf083b32d78ccf4b6e8e744d07e0b9 Mon Sep 17 00:00:00 2001 From: reya Date: Fri, 20 Dec 2024 10:53:45 +0700 Subject: [PATCH] chore: clean up --- Cargo.lock | 88 +------ crates/ui/Cargo.toml | 2 - crates/ui/src/lib.rs | 4 - crates/ui/src/svg_img.rs | 301 ----------------------- crates/ui/src/virtual_list.rs | 447 ---------------------------------- 5 files changed, 7 insertions(+), 835 deletions(-) delete mode 100644 crates/ui/src/svg_img.rs delete mode 100644 crates/ui/src/virtual_list.rs diff --git a/Cargo.lock b/Cargo.lock index beb9ee3..ea40ba0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1120,14 +1120,12 @@ dependencies = [ "once_cell", "paste", "regex", - "resvg", "rust-embed", "serde", "serde_json", "smallvec", "smol", "unicode-segmentation", - "usvg", "uuid", ] @@ -1217,31 +1215,22 @@ dependencies = [ "libc", ] -[[package]] -name = "core_maths" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3" -dependencies = [ - "libm", -] - [[package]] name = "cosmic-text" version = "0.11.2" source = "git+https://github.com/pop-os/cosmic-text?rev=542b20c#542b20ca4376a3b5de5fa629db1a4ace44e18e0c" dependencies = [ "bitflags 2.6.0", - "fontdb 0.18.0", + "fontdb", "log", "rangemap", "rayon", "rustc-hash 1.1.0", - "rustybuzz 0.14.1", + "rustybuzz", "self_cell", "swash", "sys-locale", - "ttf-parser 0.21.1", + "ttf-parser", "unicode-bidi", "unicode-linebreak", "unicode-script", @@ -1789,20 +1778,7 @@ dependencies = [ "memmap2", "slotmap", "tinyvec", - "ttf-parser 0.21.1", -] - -[[package]] -name = "fontdb" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a6f9af55fb97ad673fb7a69533eb2f967648a06fa21f8c9bb2cd6d33975716" -dependencies = [ - "fontconfig-parser", - "log", - "slotmap", - "tinyvec", - "ttf-parser 0.24.1", + "ttf-parser", ] [[package]] @@ -4462,27 +4438,9 @@ dependencies = [ "bytemuck", "libm", "smallvec", - "ttf-parser 0.21.1", - "unicode-bidi-mirroring 0.2.0", - "unicode-ccc 0.2.0", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "rustybuzz" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85d1ccd519e61834798eb52c4e886e8c2d7d698dd3d6ce0b1b47eb8557f1181" -dependencies = [ - "bitflags 2.6.0", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser 0.24.1", - "unicode-bidi-mirroring 0.3.0", - "unicode-ccc 0.3.0", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", "unicode-properties", "unicode-script", ] @@ -5559,15 +5517,6 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" -[[package]] -name = "ttf-parser" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" -dependencies = [ - "core_maths", -] - [[package]] name = "tungstenite" version = "0.24.0" @@ -5629,24 +5578,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" - [[package]] name = "unicode-ccc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" -[[package]] -name = "unicode-ccc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" - [[package]] name = "unicode-ident" version = "1.0.14" @@ -5686,12 +5623,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" version = "0.1.14" @@ -5741,21 +5672,16 @@ dependencies = [ "base64", "data-url", "flate2", - "fontdb 0.22.0", "imagesize", "kurbo", "log", "pico-args", "roxmltree", - "rustybuzz 0.18.0", "simplecss", "siphasher 1.0.1", "strict-num", "svgtypes", "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", "xmlwriter", ] diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index 763b37d..7c073d7 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -22,5 +22,3 @@ unicode-segmentation = "1.11.0" uuid = "1.10" once_cell = "1.19.0" image = "0.25.1" -usvg = { version = "0.44.0", default-features = false, features = ["system-fonts", "text"] } -resvg = { version = "0.44.0", default-features = false, features = ["system-fonts", "text"] } diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index 02ab26d..b6efa16 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -4,7 +4,6 @@ mod focusable; mod icon; mod root; mod styled; -mod svg_img; mod title_bar; pub mod accordion; @@ -44,7 +43,6 @@ pub mod switch; pub mod tab; pub mod theme; pub mod tooltip; -pub mod virtual_list; pub use crate::Disableable; pub use event::InteractiveElementExt; @@ -52,11 +50,9 @@ pub use focusable::FocusableCycle; pub use root::{ContextModal, Root}; pub use styled::*; pub use title_bar::*; -pub use virtual_list::{h_virtual_list, v_virtual_list, VirtualList}; pub use colors::*; pub use icon::*; -pub use svg_img::*; use rust_embed::RustEmbed; diff --git a/crates/ui/src/svg_img.rs b/crates/ui/src/svg_img.rs deleted file mode 100644 index 550e536..0000000 --- a/crates/ui/src/svg_img.rs +++ /dev/null @@ -1,301 +0,0 @@ -use std::{ - hash::Hash, - ops::Deref, - sync::{Arc, LazyLock}, -}; - -use gpui::{ - px, size, AppContext, Asset, Bounds, Element, Hitbox, ImageCacheError, InteractiveElement, - Interactivity, IntoElement, IsZero, Pixels, RenderImage, SharedString, Size, StyleRefinement, - Styled, WindowContext, -}; -use image::Frame; -use smallvec::SmallVec; - -use image::ImageBuffer; - -use crate::Assets; - -const SCALE: f32 = 2.; -const FONT_PATH: &str = "fonts/NotoSans-Regular.ttf"; -static OPTIONS: LazyLock = LazyLock::new(|| { - let mut options = usvg::Options::default(); - if let Some(font_data) = Assets::get(FONT_PATH).map(|f| f.data) { - options.fontdb_mut().load_font_data(font_data.into()); - } - options -}); - -#[derive(Debug, Clone, Hash)] -pub enum SvgSource { - /// A svg bytes - Data(Arc<[u8]>), - /// An asset path - Path(SharedString), -} - -impl From<&[u8]> for SvgSource { - fn from(data: &[u8]) -> Self { - Self::Data(data.into()) - } -} - -impl From> for SvgSource { - fn from(data: Arc<[u8]>) -> Self { - Self::Data(data) - } -} - -impl From for SvgSource { - fn from(path: SharedString) -> Self { - Self::Path(path) - } -} - -impl From<&'static str> for SvgSource { - fn from(path: &'static str) -> Self { - Self::Path(path.into()) - } -} - -impl Clone for SvgImg { - fn clone(&self) -> Self { - Self { - interactivity: Interactivity::default(), - source: self.source.clone(), - size: self.size, - } - } -} - -enum Image {} - -#[derive(Debug, Clone)] -struct ImageSource { - source: SvgSource, - size: Size, -} - -impl Hash for ImageSource { - /// Hash to to control the Asset cache - fn hash(&self, state: &mut H) { - self.source.hash(state); - } -} - -impl Asset for Image { - type Source = ImageSource; - type Output = Result, ImageCacheError>; - - fn load( - source: Self::Source, - cx: &mut AppContext, - ) -> impl std::future::Future + Send + 'static { - let asset_source = cx.asset_source().clone(); - - async move { - let size = source.size; - if size.width.is_zero() || size.height.is_zero() { - return Err(usvg::Error::InvalidSize.into()); - } - let size = Size { - width: (size.width * SCALE).ceil(), - height: (size.height * SCALE).ceil(), - }; - - let bytes = match source.source { - SvgSource::Data(data) => data, - SvgSource::Path(path) => { - if let Ok(Some(data)) = asset_source.load(&path) { - data.deref().to_vec().into() - } else { - Err(std::io::Error::other(format!( - "failed to load svg image from path: {}", - path - ))) - .map_err(|e| ImageCacheError::Io(Arc::new(e)))? - } - } - }; - - let tree = usvg::Tree::from_data(&bytes, &OPTIONS)?; - - let mut pixmap = - resvg::tiny_skia::Pixmap::new(size.width.0 as u32, size.height.0 as u32) - .ok_or(usvg::Error::InvalidSize)?; - - let transform = resvg::tiny_skia::Transform::from_scale(SCALE, SCALE); - - resvg::render(&tree, transform, &mut pixmap.as_mut()); - - let mut buffer = ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take()) - .expect("invalid svg image buffer"); - - // Convert from RGBA with premultiplied alpha to BGRA with straight alpha. - for pixel in buffer.chunks_exact_mut(4) { - pixel.swap(0, 2); - if pixel[3] > 0 { - let a = pixel[3] as f32 / 255.; - pixel[0] = (pixel[0] as f32 / a) as u8; - pixel[1] = (pixel[1] as f32 / a) as u8; - pixel[2] = (pixel[2] as f32 / a) as u8; - } - } - - Ok(Arc::new(RenderImage::new(SmallVec::from_elem( - Frame::new(buffer), - 1, - )))) - } - } -} - -pub struct SvgImg { - interactivity: Interactivity, - source: Option, - size: Size, -} - -impl SvgImg { - /// Create a new svg image element. - /// - /// The `src_width` and `src_height` are the original width and height of the svg image. - pub fn new() -> Self { - Self { - interactivity: Interactivity::default(), - source: None, - size: Size::default(), - } - } - - /// Set the path of the svg image from the asset. - /// - /// The `size` argument is the size of the original svg image. - #[must_use] - pub fn source( - mut self, - source: impl Into, - width: impl Into, - height: impl Into, - ) -> Self { - self.source = Some(source.into()); - self.size = size(width.into(), height.into()); - self - } -} - -impl Default for SvgImg { - fn default() -> Self { - Self::new() - } -} - -impl IntoElement for SvgImg { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for SvgImg { - type RequestLayoutState = (); - type PrepaintState = Option; - - fn id(&self) -> Option { - self.interactivity.element_id.clone() - } - - fn request_layout( - &mut self, - global_id: Option<&gpui::GlobalElementId>, - cx: &mut WindowContext, - ) -> (gpui::LayoutId, Self::RequestLayoutState) { - let layout_id = self - .interactivity - .request_layout(global_id, cx, |style, cx| cx.request_layout(style, None)); - - (layout_id, ()) - } - - fn prepaint( - &mut self, - global_id: Option<&gpui::GlobalElementId>, - bounds: gpui::Bounds, - _: &mut Self::RequestLayoutState, - cx: &mut WindowContext, - ) -> Self::PrepaintState { - self.interactivity - .prepaint(global_id, bounds, bounds.size, cx, |_, _, hitbox, _| hitbox) - } - - fn paint( - &mut self, - global_id: Option<&gpui::GlobalElementId>, - bounds: gpui::Bounds, - _: &mut Self::RequestLayoutState, - hitbox: &mut Self::PrepaintState, - cx: &mut WindowContext, - ) { - let source = self.source.clone(); - - self.interactivity - .paint(global_id, bounds, hitbox.as_ref(), cx, |_style, cx| { - let size = self.size; - - let data = if let Some(source) = source { - match cx.use_asset::(&ImageSource { source, size }) { - Some(Ok(data)) => Some(data), - _ => None, - } - } else { - None - }; - - if let Some(data) = data { - // To calculate the ratio of the original image size to the container bounds size. - // Scale by shortest side (width or height) to get a fit image. - // And center the image in the container bounds. - let ratio = if bounds.size.width < bounds.size.height { - bounds.size.width / size.width - } else { - bounds.size.height / size.height - }; - - let ratio = ratio.min(1.0); - - let new_size = gpui::Size { - width: size.width * ratio, - height: size.height * ratio, - }; - let new_origin = gpui::Point { - x: bounds.origin.x + px(((bounds.size.width - new_size.width) / 2.).into()), - y: bounds.origin.y - + px(((bounds.size.height - new_size.height) / 2.).into()), - }; - - let img_bounds = Bounds { - origin: new_origin.map(|origin| origin.floor()), - size: new_size.map(|size| size.ceil()), - }; - - match cx.paint_image(img_bounds, px(0.).into(), data, 0, false) { - Ok(_) => {} - Err(err) => eprintln!("failed to paint svg image: {:?}", err), - } - } - }) - } -} - -impl Styled for SvgImg { - fn style(&mut self) -> &mut StyleRefinement { - &mut self.interactivity.base_style - } -} - -impl InteractiveElement for SvgImg { - fn interactivity(&mut self) -> &mut Interactivity { - &mut self.interactivity - } -} diff --git a/crates/ui/src/virtual_list.rs b/crates/ui/src/virtual_list.rs deleted file mode 100644 index ba5be48..0000000 --- a/crates/ui/src/virtual_list.rs +++ /dev/null @@ -1,447 +0,0 @@ -//! Vistual List for render a large number of differently sized rows/columns. -//! -//! > NOTE: This must ensure each column width or row height. -//! -//! Only visible range are rendered for performance reasons. -//! -//! Inspired by `gpui::uniform_list`. -//! https://github.com/zed-industries/zed/blob/0ae1603610ab6b265bdfbee7b8dbc23c5ab06edc/crates/gpui/src/elements/uniform_list.rs -//! -//! Unlike the `uniform_list`, the each item can have different size. -//! -//! This is useful for more complex layout, for example, a table with different row height. -use std::{cmp, ops::Range, rc::Rc}; - -use gpui::{ - div, point, px, size, AnyElement, AvailableSpace, Axis, Bounds, ContentMask, Div, Element, - ElementId, GlobalElementId, Hitbox, InteractiveElement, IntoElement, IsZero as _, Pixels, - Render, ScrollHandle, Size, Stateful, StatefulInteractiveElement, StyleRefinement, Styled, - View, ViewContext, WindowContext, -}; -use smallvec::SmallVec; - -/// Create a virtual list in Vertical direction. -/// -/// This is like `uniform_list` in GPUI, but support two axis. -/// -/// The `item_sizes` is the size of each column. -pub fn v_virtual_list( - view: View, - id: impl Into, - item_sizes: Rc>>, - f: impl 'static + Fn(&mut V, Range, Size, &mut ViewContext) -> Vec, -) -> VirtualList -where - R: IntoElement, - V: Render, -{ - virtual_list(view, id, Axis::Vertical, item_sizes, f) -} - -/// Create a virtual list in Horizontal direction. -pub fn h_virtual_list( - view: View, - id: impl Into, - item_sizes: Rc>>, - f: impl 'static + Fn(&mut V, Range, Size, &mut ViewContext) -> Vec, -) -> VirtualList -where - R: IntoElement, - V: Render, -{ - virtual_list(view, id, Axis::Horizontal, item_sizes, f) -} - -pub(crate) fn virtual_list( - view: View, - id: impl Into, - axis: Axis, - item_sizes: Rc>>, - f: impl 'static + Fn(&mut V, Range, Size, &mut ViewContext) -> Vec, -) -> VirtualList -where - R: IntoElement, - V: Render, -{ - let id: ElementId = id.into(); - let scroll_handle = ScrollHandle::default(); - let render_range = move |visible_range, content_size, cx: &mut WindowContext| { - view.update(cx, |this, cx| { - f(this, visible_range, content_size, cx) - .into_iter() - .map(|component| component.into_any_element()) - .collect() - }) - }; - - VirtualList { - id: id.clone(), - axis, - base: div() - .id(id) - .size_full() - .overflow_scroll() - .track_scroll(&scroll_handle), - scroll_handle, - items_count: item_sizes.len(), - item_sizes, - render_items: Box::new(render_range), - } -} - -type RenderItems = Box< - dyn for<'a> Fn(Range, Size, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]>, ->; - -/// VirtualItem component for rendering a large number of differently sized columns. -pub struct VirtualList { - id: ElementId, - axis: Axis, - base: Stateful
, - scroll_handle: ScrollHandle, - // scroll_handle: ScrollHandle, - items_count: usize, - item_sizes: Rc>>, - render_items: RenderItems, -} - -impl Styled for VirtualList { - fn style(&mut self) -> &mut StyleRefinement { - self.base.style() - } -} - -impl VirtualList { - pub fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self { - self.base = self.base.track_scroll(scroll_handle); - self.scroll_handle = scroll_handle.clone(); - self - } - - /// Specify for table - #[allow(dead_code)] - pub(crate) fn with_scroll_handle(mut self, scroll_handle: &ScrollHandle) -> Self { - self.base = div().id(self.id.clone()).size_full(); - self.scroll_handle = scroll_handle.clone(); - self - } - - /// Measure first item to get the size. - fn measure_item(&self, cx: &mut WindowContext) -> Size { - if self.items_count == 0 { - return Size::default(); - } - - let item_ix = 0; - let mut items = (self.render_items)(item_ix..item_ix + 1, Size::default(), cx); - let Some(mut item_to_measure) = items.pop() else { - return Size::default(); - }; - let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent); - item_to_measure.layout_as_root(available_space, cx) - } -} - -/// Frame state used by the [VirtualItem]. -pub struct VirtualListFrameState { - /// Visible items to be painted. - items: SmallVec<[AnyElement; 32]>, - item_sizes: Vec, - item_origins: Vec, -} - -impl IntoElement for VirtualList { - type Element = Self; - - fn into_element(self) -> Self::Element { - self - } -} - -impl Element for VirtualList { - type RequestLayoutState = VirtualListFrameState; - type PrepaintState = Option; - - fn id(&self) -> Option { - Some(self.id.clone()) - } - - fn request_layout( - &mut self, - global_id: Option<&GlobalElementId>, - cx: &mut WindowContext, - ) -> (gpui::LayoutId, Self::RequestLayoutState) { - let style = self.base.interactivity().compute_style(global_id, None, cx); - let font_size = cx.text_style().font_size.to_pixels(cx.rem_size()); - - // Including the gap between items for calculate the item size - let gap = match self.axis { - Axis::Horizontal => style.gap.width, - Axis::Vertical => style.gap.height, - } - .to_pixels(font_size.into(), cx.rem_size()); - - // TODO: To cache the item_sizes, item_origins - // If there have 500,000 items, this method will speed about 500~600µs - // let start = std::time::Instant::now(); - // Prepare each item's size by axis - let item_sizes = match self.axis { - Axis::Horizontal => self - .item_sizes - .iter() - .enumerate() - .map(|(i, size)| { - if i == self.items_count - 1 { - size.width - } else { - size.width + gap - } - }) - .collect::>(), - Axis::Vertical => self - .item_sizes - .iter() - .enumerate() - .map(|(i, size)| { - if i == self.items_count - 1 { - size.height - } else { - size.height + gap - } - }) - .collect::>(), - }; - - // Prepare each item's origin by axis - let item_origins = match self.axis { - Axis::Horizontal => item_sizes - .iter() - .scan(px(0.), |cumulative_x, size| { - let x = *cumulative_x; - *cumulative_x += *size; - Some(x) - }) - .collect::>(), - Axis::Vertical => item_sizes - .iter() - .scan(px(0.), |cumulative_y, size| { - let y = *cumulative_y; - *cumulative_y += *size; - Some(y) - }) - .collect::>(), - }; - // println!("layout: {} {:?}", item_sizes.len(), start.elapsed()); - - let (layout_id, _) = self.base.request_layout(global_id, cx); - - ( - layout_id, - VirtualListFrameState { - items: SmallVec::new(), - item_sizes, - item_origins, - }, - ) - } - - fn prepaint( - &mut self, - global_id: Option<&GlobalElementId>, - bounds: Bounds, - layout: &mut Self::RequestLayoutState, - cx: &mut WindowContext, - ) -> Self::PrepaintState { - let style = self.base.interactivity().compute_style(global_id, None, cx); - let border = style.border_widths.to_pixels(cx.rem_size()); - let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size()); - - let first_item_size = self.measure_item(cx); - - let padded_bounds = Bounds::from_corners( - bounds.origin + point(border.left + padding.left, border.top + padding.top), - bounds.bottom_right() - - point(border.right + padding.right, border.bottom + padding.bottom), - ); - - // Get border + padding pixel size - let padding_size = match self.axis { - Axis::Horizontal => border.left + padding.left + border.right + padding.right, - Axis::Vertical => border.top + padding.top + border.bottom + padding.bottom, - }; - - let item_sizes = &layout.item_sizes; - let item_origins = &layout.item_origins; - - let content_size = match self.axis { - Axis::Horizontal => Size { - width: px(item_sizes.iter().map(|size| size.0).sum::()) + padding_size, - height: (first_item_size.height + padding_size).max(padded_bounds.size.height), - }, - Axis::Vertical => Size { - width: (first_item_size.width + padding_size).max(padded_bounds.size.width), - height: px(item_sizes.iter().map(|size| size.0).sum::()) + padding_size, - }, - }; - - self.base.interactivity().prepaint( - global_id, - bounds, - content_size, - cx, - |style, _, hitbox, cx| { - let mut scroll_offset = self.scroll_handle.offset(); - let border = style.border_widths.to_pixels(cx.rem_size()); - let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size()); - - let padded_bounds = Bounds::from_corners( - bounds.origin + point(border.left + padding.left, border.top), - bounds.bottom_right() - point(border.right + padding.right, border.bottom), - ); - - if self.items_count > 0 { - let is_scrolled = match self.axis { - Axis::Horizontal => !scroll_offset.x.is_zero(), - Axis::Vertical => !scroll_offset.y.is_zero(), - }; - - let min_scroll_offset = match self.axis { - Axis::Horizontal => padded_bounds.size.width - content_size.width, - Axis::Vertical => padded_bounds.size.height - content_size.height, - }; - - if is_scrolled { - match self.axis { - Axis::Horizontal if scroll_offset.x < min_scroll_offset => { - scroll_offset.x = min_scroll_offset; - } - Axis::Vertical if scroll_offset.y < min_scroll_offset => { - scroll_offset.y = min_scroll_offset; - } - _ => {} - } - } - - let (first_visible_element_ix, last_visible_element_ix) = match self.axis { - Axis::Horizontal => { - let mut cumulative_size = px(0.); - let mut first_visible_element_ix = 0; - for (i, &size) in item_sizes.iter().enumerate() { - cumulative_size += size; - if cumulative_size > -(scroll_offset.x + padding.left) { - first_visible_element_ix = i; - break; - } - } - - cumulative_size = px(0.); - let mut last_visible_element_ix = 0; - for (i, &size) in item_sizes.iter().enumerate() { - cumulative_size += size; - if cumulative_size > (-scroll_offset.x + padded_bounds.size.width) { - last_visible_element_ix = i + 1; - break; - } - } - if last_visible_element_ix == 0 { - last_visible_element_ix = self.items_count; - } else { - last_visible_element_ix += 1; - } - (first_visible_element_ix, last_visible_element_ix) - } - Axis::Vertical => { - let mut cumulative_size = px(0.); - let mut first_visible_element_ix = 0; - for (i, &size) in item_sizes.iter().enumerate() { - cumulative_size += size; - if cumulative_size > -(scroll_offset.y + padding.top) { - first_visible_element_ix = i; - break; - } - } - - cumulative_size = px(0.); - let mut last_visible_element_ix = 0; - for (i, &size) in item_sizes.iter().enumerate() { - cumulative_size += size; - if cumulative_size > (-scroll_offset.y + padded_bounds.size.height) - { - last_visible_element_ix = i + 1; - break; - } - } - if last_visible_element_ix == 0 { - last_visible_element_ix = self.items_count; - } else { - last_visible_element_ix += 1; - } - (first_visible_element_ix, last_visible_element_ix) - } - }; - - let visible_range = first_visible_element_ix - ..cmp::min(last_visible_element_ix, self.items_count); - - let items = (self.render_items)(visible_range.clone(), content_size, cx); - - let content_mask = ContentMask { bounds }; - cx.with_content_mask(Some(content_mask), |cx| { - for (mut item, ix) in items.into_iter().zip(visible_range.clone()) { - let item_origin = match self.axis { - Axis::Horizontal => { - padded_bounds.origin - + point( - item_origins[ix] + scroll_offset.x, - padding.top + scroll_offset.y, - ) - } - Axis::Vertical => { - padded_bounds.origin - + point( - scroll_offset.x, - padding.top + item_origins[ix] + scroll_offset.y, - ) - } - }; - - let available_space = match self.axis { - Axis::Horizontal => size( - AvailableSpace::Definite(item_sizes[ix]), - AvailableSpace::Definite(padded_bounds.size.height), - ), - Axis::Vertical => size( - AvailableSpace::Definite(padded_bounds.size.width), - AvailableSpace::Definite(item_sizes[ix]), - ), - }; - - item.layout_as_root(available_space, cx); - item.prepaint_at(item_origin, cx); - layout.items.push(item); - } - }); - } - - hitbox - }, - ) - } - - fn paint( - &mut self, - global_id: Option<&GlobalElementId>, - bounds: Bounds, - layout: &mut Self::RequestLayoutState, - hitbox: &mut Self::PrepaintState, - cx: &mut WindowContext, - ) { - self.base - .interactivity() - .paint(global_id, bounds, hitbox.as_ref(), cx, |_, cx| { - for item in &mut layout.items { - item.paint(cx); - } - }) - } -}