diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index 2ec307bbc3..6afe80ce06 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -2,8 +2,8 @@ use std::{any::Any, f32::INFINITY}; use crate::{ json::{self, ToJson, Value}, - Axis, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, - SizeConstraint, Vector2FExt, + Axis, DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle, Event, + EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt, }; use pathfinder_geometry::{ rect::RectF, @@ -11,9 +11,15 @@ use pathfinder_geometry::{ }; use serde_json::json; +#[derive(Default)] +struct ScrollState { + scroll_position: f32, +} + pub struct Flex { axis: Axis, children: Vec, + scroll_state: Option>, } impl Flex { @@ -21,6 +27,7 @@ impl Flex { Self { axis, children: Default::default(), + scroll_state: None, } } @@ -32,6 +39,15 @@ impl Flex { Self::new(Axis::Vertical) } + pub fn scrollable(mut self, element_id: usize, cx: &mut C) -> Self + where + Tag: 'static, + C: ElementStateContext, + { + self.scroll_state = Some(cx.element_state::(element_id)); + self + } + fn layout_flex_children( &mut self, layout_expanded: bool, @@ -167,6 +183,13 @@ impl Element for Flex { size.set_y(constraint.max.y()); } + if let Some(scroll_state) = self.scroll_state.as_ref() { + scroll_state.update(cx, |scroll_state, _| { + scroll_state.scroll_position = + scroll_state.scroll_position.min(-remaining_space).max(0.); + }); + } + (size, remaining_space) } @@ -181,7 +204,16 @@ impl Element for Flex { if overflowing { cx.scene.push_layer(Some(bounds)); } + let mut child_origin = bounds.origin(); + if let Some(scroll_state) = self.scroll_state.as_ref() { + let scroll_position = scroll_state.read(cx).scroll_position; + match self.axis { + Axis::Horizontal => child_origin.set_x(child_origin.x() - scroll_position), + Axis::Vertical => child_origin.set_y(child_origin.y() - scroll_position), + } + } + for child in &mut self.children { if *remaining_space > 0. { if let Some(metadata) = child.metadata::() { @@ -208,8 +240,8 @@ impl Element for Flex { fn dispatch_event( &mut self, event: &Event, - _: RectF, - _: &mut Self::LayoutState, + bounds: RectF, + remaining_space: &mut Self::LayoutState, _: &mut Self::PaintState, cx: &mut EventContext, ) -> bool { @@ -217,6 +249,41 @@ impl Element for Flex { for child in &mut self.children { handled = child.dispatch_event(event, cx) || handled; } + if !handled { + if let &Event::ScrollWheel { + position, + delta, + precise, + } = event + { + if *remaining_space < 0. && bounds.contains_point(position) { + if let Some(scroll_state) = self.scroll_state.as_ref() { + scroll_state.update(cx, |scroll_state, cx| { + dbg!(precise, delta); + + let mut delta = match self.axis { + Axis::Horizontal => { + if delta.x() != 0. { + delta.x() + } else { + delta.y() + } + } + Axis::Vertical => delta.y(), + }; + if !precise { + delta *= 20.; + } + + scroll_state.scroll_position -= delta; + + handled = true; + cx.notify(); + }); + } + } + } + } handled } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index fb13ef2fd0..76093883c4 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -633,7 +633,7 @@ impl Pane { enum Tabs {} let pane = cx.handle(); let tabs = MouseEventHandler::new::(0, cx, |mouse_state, cx| { - let mut row = Flex::row(); + let mut row = Flex::row().scrollable::(1, cx); for (ix, item) in self.items.iter().enumerate() { let is_active = ix == self.active_item_index;