Detailed changes
@@ -1165,10 +1165,7 @@ pub mod tests {
*markers[0].column_mut() += 1;
}
- assert_eq!(
- unmarked_snapshot.clip_point(dbg!(markers[0]), bias),
- markers[1]
- )
+ assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
}
};
}
@@ -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,16 @@ use pathfinder_geometry::{
};
use serde_json::json;
+#[derive(Default)]
+struct ScrollState {
+ scroll_to: Option<usize>,
+ scroll_position: f32,
+}
+
pub struct Flex {
axis: Axis,
children: Vec<ElementBox>,
+ scroll_state: Option<ElementStateHandle<ScrollState>>,
}
impl Flex {
@@ -21,6 +28,7 @@ impl Flex {
Self {
axis,
children: Default::default(),
+ scroll_state: None,
}
}
@@ -32,6 +40,22 @@ impl Flex {
Self::new(Axis::Vertical)
}
+ pub fn scrollable<Tag, C>(
+ mut self,
+ element_id: usize,
+ scroll_to: Option<usize>,
+ cx: &mut C,
+ ) -> Self
+ where
+ Tag: 'static,
+ C: ElementStateContext,
+ {
+ let scroll_state = cx.element_state::<Tag, ScrollState>(element_id);
+ scroll_state.update(cx, |scroll_state, _| scroll_state.scroll_to = scroll_to);
+ self.scroll_state = Some(scroll_state);
+ self
+ }
+
fn layout_flex_children(
&mut self,
layout_expanded: bool,
@@ -167,6 +191,30 @@ 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, _| {
+ if let Some(scroll_to) = scroll_state.scroll_to.take() {
+ let visible_start = scroll_state.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]
+ .iter()
+ .map(|c| c.size().along(self.axis))
+ .sum();
+ let child_end = child_start + child.size().along(self.axis);
+ if child_start < visible_start {
+ scroll_state.scroll_position = child_start;
+ } else if child_end > visible_end {
+ scroll_state.scroll_position = child_end - size.along(self.axis);
+ }
+ }
+ }
+
+ scroll_state.scroll_position =
+ scroll_state.scroll_position.min(-remaining_space).max(0.);
+ });
+ }
+
(size, remaining_space)
}
@@ -181,7 +229,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::<FlexParentData>() {
@@ -208,15 +265,54 @@ 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 {
+ if let Some(position) = event.position() {
+ if !bounds.contains_point(position) {
+ return false;
+ }
+ }
+
let mut handled = false;
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| {
+ 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
}
@@ -8,11 +8,10 @@ use crate::{
ElementBox,
};
use json::ToJson;
-use parking_lot::Mutex;
-use std::{cmp, ops::Range, sync::Arc};
+use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
#[derive(Clone, Default)]
-pub struct UniformListState(Arc<Mutex<StateInner>>);
+pub struct UniformListState(Rc<RefCell<StateInner>>);
#[derive(Debug)]
pub enum ScrollTarget {
@@ -22,11 +21,11 @@ pub enum ScrollTarget {
impl UniformListState {
pub fn scroll_to(&self, scroll_to: ScrollTarget) {
- self.0.lock().scroll_to = Some(scroll_to);
+ self.0.borrow_mut().scroll_to = Some(scroll_to);
}
pub fn scroll_top(&self) -> f32 {
- self.0.lock().scroll_top
+ self.0.borrow().scroll_top
}
}
@@ -96,7 +95,7 @@ where
delta *= 20.;
}
- let mut state = self.state.0.lock();
+ let mut state = self.state.0.borrow_mut();
state.scroll_top = (state.scroll_top - delta.y()).max(0.0).min(scroll_max);
cx.notify();
@@ -104,7 +103,7 @@ where
}
fn autoscroll(&mut self, scroll_max: f32, list_height: f32, item_height: f32) {
- let mut state = self.state.0.lock();
+ let mut state = self.state.0.borrow_mut();
if let Some(scroll_to) = state.scroll_to.take() {
let item_ix;
@@ -141,7 +140,7 @@ where
}
fn scroll_top(&self) -> f32 {
- self.state.0.lock().scroll_top
+ self.state.0.borrow().scroll_top
}
}
@@ -61,3 +61,20 @@ pub enum Event {
left_mouse_down: bool,
},
}
+
+impl Event {
+ pub fn position(&self) -> Option<Vector2F> {
+ match self {
+ Event::KeyDown { .. } => None,
+ Event::ScrollWheel { position, .. }
+ | Event::LeftMouseDown { position, .. }
+ | Event::LeftMouseUp { position }
+ | Event::LeftMouseDragged { position }
+ | Event::RightMouseDown { position, .. }
+ | Event::RightMouseUp { position }
+ | Event::NavigateMouseDown { position, .. }
+ | Event::NavigateMouseUp { position, .. }
+ | Event::MouseMoved { position, .. } => Some(*position),
+ }
+ }
+}
@@ -101,6 +101,7 @@ pub enum Event {
pub struct Pane {
items: Vec<Box<dyn ItemHandle>>,
active_item_index: usize,
+ autoscroll: bool,
nav_history: Rc<RefCell<NavHistory>>,
toolbar: ViewHandle<Toolbar>,
}
@@ -142,6 +143,7 @@ impl Pane {
Self {
items: Vec::new(),
active_item_index: 0,
+ autoscroll: false,
nav_history: Default::default(),
toolbar: cx.add_view(|_| Toolbar::new()),
}
@@ -200,27 +202,19 @@ impl Pane {
.upgrade(cx)
.and_then(|v| pane.index_for_item(v.as_ref()))
{
- if let Some(item) = pane.active_item() {
- pane.nav_history.borrow_mut().set_mode(mode);
- item.deactivated(cx);
- pane.nav_history
- .borrow_mut()
- .set_mode(NavigationMode::Normal);
- }
-
- let prev_active_index = mem::replace(&mut pane.active_item_index, index);
+ let prev_active_item_index = pane.active_item_index;
+ pane.nav_history.borrow_mut().set_mode(mode);
+ pane.activate_item(index, true, cx);
+ pane.nav_history
+ .borrow_mut()
+ .set_mode(NavigationMode::Normal);
- let mut navigated = prev_active_index != pane.active_item_index;
+ let mut navigated = prev_active_item_index != pane.active_item_index;
if let Some(data) = entry.data {
navigated |= pane.active_item()?.navigate(data, cx);
}
if navigated {
- pane.focus_active_item(cx);
- pane.update_toolbar(cx);
- pane.activate(cx);
- cx.emit(Event::ActivateItem { local: true });
- cx.notify();
break None;
}
}
@@ -376,10 +370,12 @@ impl Pane {
}
pub fn activate_item(&mut self, index: usize, local: bool, cx: &mut ViewContext<Self>) {
+ use NavigationMode::{GoingBack, GoingForward};
if index < self.items.len() {
let prev_active_item_ix = mem::replace(&mut self.active_item_index, index);
- if prev_active_item_ix != self.active_item_index
- && prev_active_item_ix < self.items.len()
+ if matches!(self.nav_history.borrow().mode, GoingBack | GoingForward)
+ || (prev_active_item_ix != self.active_item_index
+ && prev_active_item_ix < self.items.len())
{
self.items[prev_active_item_ix].deactivated(cx);
cx.emit(Event::ActivateItem { local });
@@ -389,6 +385,7 @@ impl Pane {
self.focus_active_item(cx);
self.activate(cx);
}
+ self.autoscroll = true;
cx.notify();
}
}
@@ -628,13 +625,18 @@ impl Pane {
});
}
- fn render_tabs(&self, cx: &mut RenderContext<Self>) -> ElementBox {
+ fn render_tabs(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = cx.global::<Settings>().theme.clone();
enum Tabs {}
let pane = cx.handle();
let tabs = MouseEventHandler::new::<Tabs, _, _>(0, cx, |mouse_state, cx| {
- let mut row = Flex::row();
+ let autoscroll = if mem::take(&mut self.autoscroll) {
+ Some(self.active_item_index)
+ } else {
+ None
+ };
+ let mut row = Flex::row().scrollable::<Tabs, _>(1, autoscroll, cx);
for (ix, item) in self.items.iter().enumerate() {
let is_active = ix == self.active_item_index;