Make the tabs scrollable when they overflow

This adds the ability to make a Flex element scrollable by passing a type tag and instance id, which we use to store the scroll position in an ElementStateHandle.

Still need to allow the element to auto-scroll.
This commit is contained in:
Nathan Sobo 2022-04-05 19:58:15 -06:00
parent 025d857be8
commit ab3bbe1e17
2 changed files with 72 additions and 5 deletions

View file

@ -2,8 +2,8 @@ use std::{any::Any, f32::INFINITY};
use crate::{ use crate::{
json::{self, ToJson, Value}, json::{self, ToJson, Value},
Axis, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, Axis, DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle, Event,
SizeConstraint, Vector2FExt, EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt,
}; };
use pathfinder_geometry::{ use pathfinder_geometry::{
rect::RectF, rect::RectF,
@ -11,9 +11,15 @@ use pathfinder_geometry::{
}; };
use serde_json::json; use serde_json::json;
#[derive(Default)]
struct ScrollState {
scroll_position: f32,
}
pub struct Flex { pub struct Flex {
axis: Axis, axis: Axis,
children: Vec<ElementBox>, children: Vec<ElementBox>,
scroll_state: Option<ElementStateHandle<ScrollState>>,
} }
impl Flex { impl Flex {
@ -21,6 +27,7 @@ impl Flex {
Self { Self {
axis, axis,
children: Default::default(), children: Default::default(),
scroll_state: None,
} }
} }
@ -32,6 +39,15 @@ impl Flex {
Self::new(Axis::Vertical) Self::new(Axis::Vertical)
} }
pub fn scrollable<Tag, C>(mut self, element_id: usize, cx: &mut C) -> Self
where
Tag: 'static,
C: ElementStateContext,
{
self.scroll_state = Some(cx.element_state::<Tag, ScrollState>(element_id));
self
}
fn layout_flex_children( fn layout_flex_children(
&mut self, &mut self,
layout_expanded: bool, layout_expanded: bool,
@ -167,6 +183,13 @@ impl Element for Flex {
size.set_y(constraint.max.y()); 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) (size, remaining_space)
} }
@ -181,7 +204,16 @@ impl Element for Flex {
if overflowing { if overflowing {
cx.scene.push_layer(Some(bounds)); cx.scene.push_layer(Some(bounds));
} }
let mut child_origin = bounds.origin(); 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 { for child in &mut self.children {
if *remaining_space > 0. { if *remaining_space > 0. {
if let Some(metadata) = child.metadata::<FlexParentData>() { if let Some(metadata) = child.metadata::<FlexParentData>() {
@ -208,8 +240,8 @@ impl Element for Flex {
fn dispatch_event( fn dispatch_event(
&mut self, &mut self,
event: &Event, event: &Event,
_: RectF, bounds: RectF,
_: &mut Self::LayoutState, remaining_space: &mut Self::LayoutState,
_: &mut Self::PaintState, _: &mut Self::PaintState,
cx: &mut EventContext, cx: &mut EventContext,
) -> bool { ) -> bool {
@ -217,6 +249,41 @@ impl Element for Flex {
for child in &mut self.children { for child in &mut self.children {
handled = child.dispatch_event(event, cx) || handled; 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 handled
} }

View file

@ -633,7 +633,7 @@ impl Pane {
enum Tabs {} enum Tabs {}
let pane = cx.handle(); let pane = cx.handle();
let tabs = MouseEventHandler::new::<Tabs, _, _>(0, cx, |mouse_state, cx| { let tabs = MouseEventHandler::new::<Tabs, _, _>(0, cx, |mouse_state, cx| {
let mut row = Flex::row(); let mut row = Flex::row().scrollable::<Tabs, _>(1, cx);
for (ix, item) in self.items.iter().enumerate() { for (ix, item) in self.items.iter().enumerate() {
let is_active = ix == self.active_item_index; let is_active = ix == self.active_item_index;