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:
parent
025d857be8
commit
ab3bbe1e17
2 changed files with 72 additions and 5 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue