1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
use crate::{ messenger::MessageData, widget::{ component::interactive::navigation::{use_nav_scroll_view, NavJump, NavScroll, NavSignal}, context::WidgetMountOrChangeContext, utils::Vec2, WidgetId, WidgetIdOrRef, }, widget_hook, }; use serde::{Deserialize, Serialize}; fn is_zero(v: &Vec2) -> bool { v.x.abs() < 1.0e-6 && v.y.abs() < 1.0e-6 } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct ScrollViewState { #[serde(default)] pub value: Vec2, #[serde(default)] pub size_factor: Vec2, } implement_props_data!(ScrollViewState); #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ScrollViewRange { #[serde(default)] #[serde(skip_serializing_if = "is_zero")] pub from: Vec2, #[serde(default)] #[serde(skip_serializing_if = "is_zero")] pub to: Vec2, } implement_props_data!(ScrollViewRange); impl Default for ScrollViewRange { fn default() -> Self { Self { from: Vec2 { x: 0.0, y: 0.0 }, to: Vec2 { x: 1.0, y: 1.0 }, } } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct ScrollViewNotifyProps( #[serde(default)] #[serde(skip_serializing_if = "WidgetIdOrRef::is_none")] pub WidgetIdOrRef, ); implement_props_data!(ScrollViewNotifyProps); #[derive(Debug, Clone)] pub struct ScrollViewNotifyMessage { pub sender: WidgetId, pub state: ScrollViewState, } implement_message_data!(ScrollViewNotifyMessage); widget_hook! { pub use_scroll_view_notified_state(life_cycle) { life_cycle.change(|context| { for msg in context.messenger.messages { if let Some(msg) = msg.as_any().downcast_ref::<ScrollViewNotifyMessage>() { drop(context.state.write_with(msg.state.clone())); } } }); } } widget_hook! { pub use_scroll_view(life_cycle) [use_nav_scroll_view] { fn notify<T>(context: &WidgetMountOrChangeContext, data: T) where T: 'static + MessageData, { if let Ok(notify) = context.props.read::<ScrollViewNotifyProps>() { if let Some(to) = notify.0.read() { context.messenger.write(to, data); } } } life_cycle.mount(|context| { notify(&context, ScrollViewNotifyMessage { sender: context.id.to_owned(), state: ScrollViewState::default(), }); drop(context.state.write_with(ScrollViewState::default())); }); life_cycle.change(|context| { let mut data = context.state.read_cloned_or_default::<ScrollViewState>(); let range = context.props.read::<ScrollViewRange>(); let mut dirty = false; for msg in context.messenger.messages { if let Some( NavSignal::Jump(NavJump::Scroll(NavScroll::Change(value, factor, relative))) ) = msg.as_any().downcast_ref() { if *relative { data.value.x += value.x; data.value.y += value.y; } else { data.value = *value; } if let Ok(range) = &range { data.value.x = data.value.x.max(range.from.x).min(range.to.x); data.value.y = data.value.y.max(range.from.y).min(range.to.y); } data.size_factor = *factor; dirty = true; } } if dirty { notify(&context, ScrollViewNotifyMessage { sender: context.id.to_owned(), state: data.clone(), }); drop(context.state.write_with(data)); } }); } }