Detailed changes
@@ -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)
}
}
@@ -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);
+ }
+ });
})
})
});
@@ -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
@@ -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 {
@@ -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) {
@@ -26,6 +26,10 @@ impl Line {
)
}
+ pub fn wrap_count(&self) -> usize {
+ self.layout.wrap_boundaries.len()
+ }
+
pub fn paint(
&self,
origin: Point<Pixels>,
@@ -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);
});
});
}
@@ -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,
@@ -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)
}
}
};
@@ -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::*;
@@ -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()
+ })))
+ })
+ }
+}
@@ -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()