From f4d4ea41232cc55a9612ad6c66ca8983201274f5 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 21 Sep 2022 23:26:42 -0700 Subject: [PATCH] WIP fixing scrollable flex --- crates/gpui/src/elements/flex.rs | 141 +++++++++++++---------- crates/gpui/src/elements/uniform_list.rs | 2 +- 2 files changed, 81 insertions(+), 62 deletions(-) diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index e68810fb36..bc3b352b6c 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -1,10 +1,10 @@ -use std::{any::Any, f32::INFINITY, ops::Range}; +use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc}; use crate::{ json::{self, ToJson, Value}, presenter::MeasurementContext, Axis, DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext, - LayoutContext, MouseMovedEvent, PaintContext, RenderContext, ScrollWheelEvent, SizeConstraint, + LayoutContext, MouseRegion, PaintContext, RenderContext, ScrollWheelEvent, SizeConstraint, Vector2FExt, View, }; use pathfinder_geometry::{ @@ -13,7 +13,7 @@ use pathfinder_geometry::{ }; use serde_json::json; -#[derive(Default)] +#[derive(Default, Clone, Copy)] struct ScrollState { scroll_to: Option, scroll_position: f32, @@ -22,7 +22,7 @@ struct ScrollState { pub struct Flex { axis: Axis, children: Vec, - scroll_state: Option>, + scroll_state: Option<(ElementStateHandle>>, usize)>, } impl Flex { @@ -52,9 +52,15 @@ impl Flex { Tag: 'static, V: View, { - let scroll_state = cx.default_element_state::(element_id); - scroll_state.update(cx, |scroll_state, _| scroll_state.scroll_to = scroll_to); - self.scroll_state = Some(scroll_state); + let scroll_state_handle = + cx.default_element_state::>>(element_id); + let scroll_state_cell = scroll_state_handle.read(cx); + let mut scroll_state = scroll_state_cell.get(); + scroll_state.scroll_to = scroll_to; + scroll_state_cell.set(scroll_state); + + self.scroll_state = Some((scroll_state_handle, cx.handle().id())); + self } @@ -100,6 +106,38 @@ impl Flex { } } } + + fn handle_scroll( + e: ScrollWheelEvent, + axis: Axis, + scroll_state: Rc>, + remaining_space: f32, + ) -> bool { + let precise = e.precise; + let delta = e.delta; + if remaining_space < 0. { + let mut delta = match axis { + Axis::Horizontal => { + if delta.x() != 0. { + delta.x() + } else { + delta.y() + } + } + Axis::Vertical => delta.y(), + }; + if !precise { + delta *= 20.; + } + + let mut old_state = scroll_state.get(); + old_state.scroll_position -= delta; + scroll_state.set(old_state); + + return true; + } + return false; + } } impl Extend for Flex { @@ -202,9 +240,9 @@ impl Element for Flex { } if let Some(scroll_state) = self.scroll_state.as_ref() { - scroll_state.update(cx, |scroll_state, _| { - if let Some(scroll_to) = scroll_state.scroll_to.take() { - let visible_start = scroll_state.scroll_position; + scroll_state.0.update(cx, |scroll_state, _| { + if let Some(scroll_to) = scroll_state.get().scroll_to.take() { + let visible_start = scroll_state.get().scroll_position; let visible_end = visible_start + size.along(self.axis); if let Some(child) = self.children.get(scroll_to) { let child_start: f32 = self.children[..scroll_to] @@ -212,16 +250,20 @@ impl Element for Flex { .map(|c| c.size().along(self.axis)) .sum(); let child_end = child_start + child.size().along(self.axis); + + let mut old_state = scroll_state.get(); if child_start < visible_start { - scroll_state.scroll_position = child_start; + old_state.scroll_position = child_start; } else if child_end > visible_end { - scroll_state.scroll_position = child_end - size.along(self.axis); + old_state.scroll_position = child_end - size.along(self.axis); } + scroll_state.set(old_state); } } - scroll_state.scroll_position = - scroll_state.scroll_position.min(-remaining_space).max(0.); + let mut old_state = scroll_state.get(); + old_state.scroll_position = old_state.scroll_position.min(-remaining_space).max(0.); + scroll_state.set(old_state); }); } @@ -242,9 +284,30 @@ impl Element for Flex { cx.scene.push_layer(Some(bounds)); } + if let Some(scroll_state) = &self.scroll_state { + cx.scene.push_mouse_region( + MouseRegion::new::(scroll_state.1, 0, bounds) + .on_scroll({ + let axis = self.axis; + let scroll_state = scroll_state.0.read(cx).clone(); + move |e, cx| { + if Self::handle_scroll( + e.platform_event, + axis, + scroll_state.clone(), + remaining_space, + ) { + cx.propogate_event(); + } + } + }) + .on_move(|_, _| { /* Eat move events so they don't propogate */ }), + ); + } + 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; + let scroll_position = scroll_state.0.read(cx).get().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), @@ -278,9 +341,9 @@ impl Element for Flex { fn dispatch_event( &mut self, event: &Event, - bounds: RectF, _: RectF, - remaining_space: &mut Self::LayoutState, + _: RectF, + _: &mut Self::LayoutState, _: &mut Self::PaintState, cx: &mut EventContext, ) -> bool { @@ -288,50 +351,6 @@ impl Element for Flex { for child in &mut self.children { handled = child.dispatch_event(event, cx) || handled; } - if !handled { - if let &Event::ScrollWheel(ScrollWheelEvent { - 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| { - 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(); - }); - } - } - } - } - - if !handled { - if let &Event::MouseMoved(MouseMovedEvent { position, .. }) = event { - // If this is a scrollable flex, and the mouse is over it, eat the scroll event to prevent - // propogating it to the element below. - if self.scroll_state.is_some() && bounds.contains_point(position) { - handled = true; - } - } - } handled } diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index 718a9fe8a2..6bc35c0692 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -287,7 +287,7 @@ impl Element for UniformList { cx.scene.push_layer(Some(bounds)); cx.scene.push_mouse_region( - MouseRegion::new::(self.view_id, 0, bounds).on_scroll({ + MouseRegion::new::(self.view_id, 0, visible_bounds).on_scroll({ let scroll_max = layout.scroll_max; let state = self.state.clone(); move |ScrollWheelRegionEvent {