From 7a39d6d4bd3429891c963d7b2735ab0b9f5a4556 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Fri, 17 Nov 2023 00:02:58 +0000 Subject: [PATCH] Enable scroll_view widget --- src/view/mod.rs | 3 +- src/view/scroll_view.rs | 29 ++++---- src/widget/mod.rs | 3 +- src/widget/scroll_view.rs | 143 +++++++++++++++++++++++--------------- 4 files changed, 105 insertions(+), 73 deletions(-) diff --git a/src/view/mod.rs b/src/view/mod.rs index 3b026bf2c..d50ef4fc9 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -16,7 +16,7 @@ mod button; // mod layout_observer; // mod list; -// mod scroll_view; +mod scroll_view; mod text; // mod use_state; mod linear_layout; @@ -30,6 +30,7 @@ pub use xilem_core::{Id, IdPath, VecSplice}; pub use button::button; pub use linear_layout::{h_stack, v_stack, LinearLayout}; pub use list::{list, List}; +pub use scroll_view::{scroll_view, ScrollView}; pub use switch::switch; pub use view::{Adapt, AdaptState, Cx, Memoize, View, ViewMarker, ViewSequence}; diff --git a/src/view/scroll_view.rs b/src/view/scroll_view.rs index f9a64d9ea..2f94d0633 100644 --- a/src/view/scroll_view.rs +++ b/src/view/scroll_view.rs @@ -14,9 +14,11 @@ use std::{any::Any, marker::PhantomData}; -use crate::{event::EventResult, id::Id, View}; +use crate::{view::View, widget::ChangeFlags, MessageResult}; -use super::Cx; +use xilem_core::Id; + +use super::{Cx, ViewMarker, ViewSequence}; pub struct ScrollView { child: C, @@ -36,18 +38,20 @@ impl ScrollView { } } +impl> ViewMarker for ScrollView {} + impl> View for ScrollView where C::Element: 'static, { type State = (Id, C::State); - type Element = crate::widget::scroll_view::ScrollView; + type Element = crate::widget::ScrollView; fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) { let (id, (child_id, child_state, child_element)) = cx.with_new_id(|cx| self.child.build(cx)); - let element = crate::widget::scroll_view::ScrollView::new(child_element); + let element = crate::widget::ScrollView::new(child_element); (id, (child_id, child_state), element) } @@ -58,27 +62,22 @@ where id: &mut Id, state: &mut Self::State, element: &mut Self::Element, - ) -> bool { + ) -> ChangeFlags { cx.with_id(*id, |cx| { let child_element = element.child_mut().downcast_mut().unwrap(); - let changed = - self.child - .rebuild(cx, &prev.child, &mut state.0, &mut state.1, child_element); - if changed { - element.child_mut().request_update(); - } - changed + self.child + .rebuild(cx, &prev.child, &mut state.0, &mut state.1, child_element) }) } - fn event( + fn message( &self, id_path: &[Id], state: &mut Self::State, event: Box, app_state: &mut T, - ) -> EventResult { + ) -> MessageResult { let tl = &id_path[1..]; - self.child.event(tl, &mut state.1, event, app_state) + self.child.message(tl, &mut state.1, event, app_state) } } diff --git a/src/widget/mod.rs b/src/widget/mod.rs index a2b73c6c5..83ec53eed 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -21,8 +21,8 @@ mod core; mod linear_layout; pub mod piet_scene_helpers; mod raw_event; +mod scroll_view; mod switch; -//mod scroll_view; mod text; #[allow(clippy::module_inception)] mod widget; @@ -34,6 +34,7 @@ pub use button::Button; pub use contexts::{AccessCx, CxState, EventCx, LayoutCx, LifeCycleCx, PaintCx, UpdateCx}; pub use linear_layout::LinearLayout; pub use raw_event::{Event, LifeCycle, MouseEvent, ViewContext}; +pub use scroll_view::ScrollView; pub use switch::Switch; pub use text::TextWidget; pub use widget::{AnyWidget, Widget}; diff --git a/src/widget/scroll_view.rs b/src/widget/scroll_view.rs index 2eeaebfd1..7fe33a784 100644 --- a/src/widget/scroll_view.rs +++ b/src/widget/scroll_view.rs @@ -17,17 +17,14 @@ //! There's a lot more functionality in the Druid version, including //! control over scrolling axes, ability to scroll to content, etc. -use druid_shell::{ - kurbo::{Affine, Point, Rect, Size, Vec2}, - piet::RenderContext, -}; +use crate::Axis; +use vello::kurbo::{Affine, Point, Size}; +use vello::peniko::Mix; +use vello::SceneBuilder; -use crate::Widget; +use super::{BoxConstraints, Widget}; -use super::{ - contexts::LifeCycleCx, EventCx, LayoutCx, LifeCycle, PaintCx, Pod, PreparePaintCx, RawEvent, - UpdateCx, -}; +use super::{contexts::LifeCycleCx, Event, EventCx, LayoutCx, LifeCycle, PaintCx, Pod, UpdateCx}; pub struct ScrollView { child: Pod, @@ -47,41 +44,26 @@ impl ScrollView { } } +// TODO: scroll bars impl Widget for ScrollView { - fn event(&mut self, cx: &mut EventCx, event: &RawEvent) { - // TODO: scroll wheel + click-drag on scroll bars - let offset = Vec2::new(0.0, self.offset); - let child_event = match event { - RawEvent::MouseDown(mouse_event) => { - let mut mouse_event = mouse_event.clone(); - mouse_event.pos += offset; - RawEvent::MouseDown(mouse_event) - } - RawEvent::MouseUp(mouse_event) => { - let mut mouse_event = mouse_event.clone(); - mouse_event.pos += offset; - RawEvent::MouseUp(mouse_event) - } - RawEvent::MouseMove(mouse_event) => { - let mut mouse_event = mouse_event.clone(); - mouse_event.pos += offset; - RawEvent::MouseMove(mouse_event) - } - RawEvent::MouseWheel(mouse_event) => { - let mut mouse_event = mouse_event.clone(); - mouse_event.pos += offset; - RawEvent::MouseWheel(mouse_event) - } - _ => event.clone(), - }; - self.child.event(cx, &child_event); + fn event(&mut self, cx: &mut EventCx, event: &Event) { + // Pass event through to child + self.child.event(cx, event); + + // Handle scroll wheel events if !cx.is_handled() { - if let RawEvent::MouseWheel(mouse) = event { - let new_offset = (self.offset + mouse.wheel_delta.y).max(0.0); + if let Event::MouseWheel(mouse) = event { + let max_offset = (self.child.get_size().height - cx.size().height).max(0.0); + let new_offset = (self.offset + mouse.wheel_delta.y).max(0.0).min(max_offset); if new_offset != self.offset { self.offset = new_offset; + let new_origin = Point { + x: 0.0, + y: -self.offset, + }; + self.child.set_origin(cx.widget_state, new_origin); cx.set_handled(true); - // TODO: request paint + cx.request_paint(); } } } @@ -95,28 +77,77 @@ impl Widget for ScrollView { self.child.update(cx); } - fn measure(&mut self, cx: &mut LayoutCx) -> (Size, Size) { - let _ = self.child.measure(cx); - (Size::ZERO, Size::new(1e9, 1e9)) + fn compute_max_intrinsic(&mut self, axis: Axis, cx: &mut LayoutCx, bc: &BoxConstraints) -> f64 { + match axis { + Axis::Horizontal => { + if bc.min().width.is_sign_negative() { + 0.0 + } else { + let length = + self.child + .compute_max_intrinsic(axis, cx, &bc.unbound_max_height()); + length.min(bc.max().width).max(bc.min().width) + } + } + Axis::Vertical => { + if bc.min().height.is_sign_negative() { + 0.0 + } else { + let length = + self.child + .compute_max_intrinsic(axis, cx, &bc.unbound_max_height()); + length.min(bc.max().height).max(bc.min().height) + } + } + } } - fn layout(&mut self, cx: &mut LayoutCx, proposed_size: Size) -> Size { - let child_proposed = Size::new(proposed_size.width, 1e9); - let child_size = self.child.layout(cx, child_proposed); - Size::new(child_size.width, proposed_size.height) + fn layout(&mut self, cx: &mut LayoutCx, bc: &BoxConstraints) -> Size { + cx.request_paint(); + + let cbc = BoxConstraints::new( + Size { + width: 0.0, + height: 0.0, + }, + Size { + width: bc.max().width, + height: f64::INFINITY, + }, + ); + let child_size = self.child.layout(cx, &cbc); + let size = Size { + width: child_size.width.min(bc.max().width), + height: child_size.height.min(bc.max().height), + }; + + // Ensure that scroll offset is within bounds + let max_offset = (child_size.height - size.height).max(0.0); + if max_offset < self.offset { + self.offset = max_offset; + let new_origin = Point { + x: 0.0, + y: -self.offset, + }; + self.child.set_origin(cx.widget_state, new_origin); + } + + size } - fn prepare_paint(&mut self, cx: &mut PreparePaintCx, visible: Rect) { - let child_visible = visible + Vec2::new(0.0, self.offset); - self.child.prepare_paint(cx, child_visible); + fn paint(&mut self, cx: &mut PaintCx, builder: &mut SceneBuilder) { + builder.push_layer(Mix::Normal, 1.0, Affine::IDENTITY, &cx.size().to_rect()); + self.child.paint(cx, builder); + builder.pop_layer(); } - fn paint(&mut self, cx: &mut PaintCx) { - cx.with_save(|cx| { - let size = cx.size(); - cx.clip(size.to_rect()); - cx.transform(Affine::translate((0.0, -self.offset))); - self.child.paint_raw(cx); - }); + fn accessibility(&mut self, cx: &mut super::AccessCx) { + self.child.accessibility(cx); + + if cx.is_requested() { + let mut builder = accesskit::NodeBuilder::new(accesskit::Role::ScrollView); + builder.set_children(vec![self.child.id().into()]); + cx.push_node(builder); + } } }