Merge branch 'gpui2' of github.com:zed-industries/zed into gpui2

Marshall Bowers created

Change summary

crates/gpui3/src/element.rs               | 23 ++++------------
crates/gpui3/src/elements/div.rs          | 10 ++++---
crates/gpui3/src/elements/text.rs         |  6 +++
crates/gpui3/src/geometry.rs              | 24 +----------------
crates/gpui3/src/interactive.rs           |  2 
crates/gpui3/src/text_system/line.rs      |  4 +++
crates/gpui3/src/view.rs                  |  5 +--
crates/gpui3/src/window.rs                | 33 +++++++++++++++++++++++-
crates/gpui3_macros/src/derive_element.rs |  2 
crates/storybook2/src/stories.rs          |  2 +
crates/storybook2/src/stories/scroll.rs   | 28 +++++++++++++++++++++
crates/storybook2/src/story_selector.rs   |  2 +
12 files changed, 90 insertions(+), 51 deletions(-)

Detailed changes

crates/gpui3/src/element.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, ViewContext};
+use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, ViewContext};
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
 use std::mem;
@@ -62,7 +62,7 @@ pub trait ParentElement: Element {
 trait ElementObject<V>: 'static + Send + Sync {
     fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
     fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
-    fn paint(&mut self, view_state: &mut V, offset: Option<Point<Pixels>>, cx: &mut ViewContext<V>);
+    fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
 }
 
 struct RenderedElement<E: Element> {
@@ -145,19 +145,13 @@ where
         layout_id
     }
 
-    fn paint(
-        &mut self,
-        view_state: &mut E::ViewState,
-        offset: Option<Point<Pixels>>,
-        cx: &mut ViewContext<E::ViewState>,
-    ) {
+    fn paint(&mut self, view_state: &mut E::ViewState, cx: &mut ViewContext<E::ViewState>) {
         self.phase = match mem::take(&mut self.phase) {
             ElementRenderPhase::LayoutRequested {
                 layout_id,
                 mut frame_state,
             } => {
-                let mut bounds = cx.layout_bounds(layout_id);
-                offset.map(|offset| bounds.origin += offset);
+                let bounds = cx.layout_bounds(layout_id);
                 if let Some(id) = self.element.id() {
                     cx.with_element_state(id, |element_state, cx| {
                         let mut element_state = element_state.unwrap();
@@ -192,13 +186,8 @@ impl<V: 'static + Send + Sync> AnyElement<V> {
         self.0.layout(view_state, cx)
     }
 
-    pub fn paint(
-        &mut self,
-        view_state: &mut V,
-        offset: Option<Point<Pixels>>,
-        cx: &mut ViewContext<V>,
-    ) {
-        self.0.paint(view_state, offset, cx)
+    pub fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
+        self.0.paint(view_state, cx)
     }
 }
 

crates/gpui3/src/elements/div.rs 🔗

@@ -277,7 +277,7 @@ where
                 for child_layout_id in &element_state.child_layout_ids {
                     let child_bounds = cx.layout_bounds(*child_layout_id);
                     child_min = child_min.min(&child_bounds.origin);
-                    child_max = child_min.max(&child_bounds.lower_right());
+                    child_max = child_max.max(&child_bounds.lower_right());
                 }
                 (child_max - child_min).into()
             };
@@ -298,9 +298,11 @@ where
                     style.apply_text_style(cx, |cx| {
                         style.apply_overflow(bounds, cx, |cx| {
                             let scroll_offset = element_state.interactive.scroll_offset();
-                            for child in &mut this.children {
-                                child.paint(view_state, scroll_offset, cx);
-                            }
+                            cx.with_scroll_offset(scroll_offset, |cx| {
+                                for child in &mut this.children {
+                                    child.paint(view_state, cx);
+                                }
+                            });
                         })
                     })
                 });

crates/gpui3/src/elements/text.rs 🔗

@@ -97,9 +97,13 @@ impl<V: 'static + Send + Sync> Element for Text<V> {
                     return Size::default();
                 };
 
+                let line_count = lines
+                    .iter()
+                    .map(|line| line.wrap_count() + 1)
+                    .sum::<usize>();
                 let size = Size {
                     width: lines.iter().map(|line| line.layout.width).max().unwrap(),
-                    height: line_height * lines.len(),
+                    height: line_height * line_count,
                 };
 
                 element_state

crates/gpui3/src/geometry.rs 🔗

@@ -4,7 +4,7 @@ use refineable::Refineable;
 use std::{
     cmp::{self, PartialOrd},
     fmt,
-    ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign},
+    ops::{Add, Div, Mul, MulAssign, Sub},
 };
 
 #[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
@@ -67,26 +67,6 @@ where
     }
 }
 
-impl<T> SubAssign<Size<T>> for Point<T>
-where
-    T: Sub<Output = T> + Clone + Debug + Default,
-{
-    fn sub_assign(&mut self, rhs: Size<T>) {
-        self.x = self.x.clone() - rhs.width;
-        self.y = self.y.clone() - rhs.height;
-    }
-}
-
-impl<T> AddAssign<T> for Point<T>
-where
-    T: Add<Output = T> + Clone + Default + Debug,
-{
-    fn add_assign(&mut self, rhs: T) {
-        self.x = self.x.clone() + rhs.clone();
-        self.y = self.y.clone() + rhs;
-    }
-}
-
 impl<T, S> Div<S> for Point<T>
 where
     T: Div<S, Output = T> + Clone + Default + Debug,
@@ -187,7 +167,7 @@ impl Size<Pixels> {
 
 impl<T> Size<T>
 where
-    T: Ord + Clone + Default + Debug,
+    T: PartialOrd + Clone + Default + Debug,
 {
     pub fn max(&self, other: &Self) -> Self {
         Size {

crates/gpui3/src/interactive.rs 🔗

@@ -477,7 +477,7 @@ pub trait ElementInteraction<V: 'static + Send + Sync>: 'static + Send + Sync {
                     .get_or_insert_with(Arc::default)
                     .clone();
                 let line_height = cx.line_height();
-                let scroll_max = content_size - bounds.size;
+                let scroll_max = (content_size - bounds.size).max(&Size::default());
 
                 cx.on_mouse_event(move |_, event: &ScrollWheelEvent, _, cx| {
                     if bounds.contains_point(&event.position) {

crates/gpui3/src/text_system/line.rs 🔗

@@ -26,6 +26,10 @@ impl Line {
         )
     }
 
+    pub fn wrap_count(&self) -> usize {
+        self.layout.wrap_boundaries.len()
+    }
+
     pub fn paint(
         &self,
         origin: Point<Pixels>,

crates/gpui3/src/view.rs 🔗

@@ -90,8 +90,7 @@ impl<V: 'static + Send + Sync> Element for View<V> {
         element: &mut Self::ElementState,
         cx: &mut ViewContext<()>,
     ) {
-        self.state
-            .update(cx, |state, cx| element.paint(state, None, cx))
+        self.state.update(cx, |state, cx| element.paint(state, cx))
     }
 }
 
@@ -186,7 +185,7 @@ impl<V: Send + Sync + 'static> ViewObject for View<V> {
         cx.with_element_id(self.entity_id(), |_global_id, cx| {
             self.state.update(cx, |state, cx| {
                 let element = element.downcast_mut::<AnyElement<V>>().unwrap();
-                element.paint(state, None, cx);
+                element.paint(state, cx);
             });
         });
     }

crates/gpui3/src/window.rs 🔗

@@ -159,6 +159,7 @@ pub struct Window {
     key_matchers: HashMap<GlobalElementId, KeyMatcher>,
     z_index_stack: StackingOrder,
     content_mask_stack: Vec<ContentMask<Pixels>>,
+    scroll_offset_stack: Vec<Point<Pixels>>,
     mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyListener)>>,
     key_dispatch_stack: Vec<KeyDispatchStackFrame>,
     freeze_key_dispatch_stack: bool,
@@ -234,6 +235,7 @@ impl Window {
             key_matchers: HashMap::default(),
             z_index_stack: StackingOrder(SmallVec::new()),
             content_mask_stack: Vec::new(),
+            scroll_offset_stack: Vec::new(),
             mouse_listeners: HashMap::default(),
             key_dispatch_stack: Vec::new(),
             freeze_key_dispatch_stack: false,
@@ -443,10 +445,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     }
 
     pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
-        self.window
+        let mut bounds = self
+            .window
             .layout_engine
             .layout_bounds(layout_id)
-            .map(Into::into)
+            .map(Into::into);
+        bounds.origin -= self.scroll_offset();
+        bounds
     }
 
     pub fn scale_factor(&self) -> f32 {
@@ -1136,6 +1141,30 @@ pub trait BorrowWindow: BorrowAppContext {
         result
     }
 
+    fn with_scroll_offset<R>(
+        &mut self,
+        offset: Option<Point<Pixels>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        let Some(offset) = offset else {
+            return f(self);
+        };
+
+        let offset = self.scroll_offset() + offset;
+        self.window_mut().scroll_offset_stack.push(offset);
+        let result = f(self);
+        self.window_mut().scroll_offset_stack.pop();
+        result
+    }
+
+    fn scroll_offset(&self) -> Point<Pixels> {
+        self.window()
+            .scroll_offset_stack
+            .last()
+            .copied()
+            .unwrap_or_default()
+    }
+
     fn with_element_state<S: 'static + Send + Sync, R>(
         &mut self,
         id: ElementId,

crates/gpui3_macros/src/derive_element.rs 🔗

@@ -86,7 +86,7 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
                 rendered_element: &mut Self::ElementState,
                 cx: &mut gpui3::ViewContext<Self::ViewState>,
             ) {
-                rendered_element.paint(view_state, None, cx)
+                rendered_element.paint(view_state, cx)
             }
         }
     };

crates/storybook2/src/stories.rs 🔗

@@ -1,9 +1,11 @@
 mod focus;
 mod kitchen_sink;
+mod scroll;
 mod text;
 mod z_index;
 
 pub use focus::*;
 pub use kitchen_sink::*;
+pub use scroll::*;
 pub use text::*;
 pub use z_index::*;

crates/storybook2/src/stories/scroll.rs 🔗

@@ -0,0 +1,28 @@
+use crate::themes::rose_pine;
+use gpui3::{div, view, Context, ParentElement, Styled, View, WindowContext};
+
+pub struct ScrollStory {
+    text: View<()>,
+}
+
+impl ScrollStory {
+    pub fn view(cx: &mut WindowContext) -> View<()> {
+        let theme = rose_pine();
+
+        view(cx.entity(|cx| ()), move |_, cx| {
+            div()
+                .id("parent")
+                .bg(theme.lowest.base.default.background)
+                .size_full()
+                .overflow_x_scroll()
+                .child(div().w_96().flex().flex_row().children((0..3).map(|ix| {
+                    let bg = if ix % 2 == 0 {
+                        theme.middle.positive.default.background
+                    } else {
+                        theme.middle.warning.default.background
+                    };
+                    div().bg(bg).flex_1().h_20()
+                })))
+        })
+    }
+}

crates/storybook2/src/story_selector.rs 🔗

@@ -19,6 +19,7 @@ pub enum ElementStory {
     Icon,
     Input,
     Label,
+    Scroll,
     Text,
     ZIndex,
 }
@@ -46,6 +47,7 @@ impl ElementStory {
             Self::Label => {
                 view(cx.entity(|cx| ()), |_, _| ui::LabelStory::new().into_any()).into_any()
             }
+            Self::Scroll => ScrollStory::view(cx).into_any(),
             Self::Text => TextStory::view(cx).into_any(),
             Self::ZIndex => {
                 view(cx.entity(|cx| ()), |_, _| ZIndexStory::new().into_any()).into_any()