///|
pub struct ScrollState {
offset_slot : State[Point]
smooth_target : State[Point?]
smooth_start : State[Point?]
smooth_start_time_ms : State[Double?]
smooth_duration_ms : State[Double?]
}
///|
pub struct FocusState {
key_slot : State[String?]
}
///|
pub struct NavigationState {
stack_slot : State[Array[String]]
}
///|
pub fn ScrollState::new(
offset? : Point = Point::new(x=0.0, y=0.0),
) -> ScrollState {
{
offset_slot: State::new(offset),
smooth_target: State::new(None),
smooth_start: State::new(None),
smooth_start_time_ms: State::new(None),
smooth_duration_ms: State::new(None),
}
}
///|
pub fn ScrollState::offset(self : ScrollState) -> Point {
self.offset_slot.get()
}
///|
pub fn ScrollState::set_offset(self : ScrollState, offset : Point) -> Unit {
self.offset_slot.set(offset)
}
///|
pub fn ScrollState::binding(self : ScrollState) -> Binding[Point] {
self.offset_slot.binding()
}
///|
pub fn ScrollState::smooth_scroll_to(
self : ScrollState,
x~ : Double,
y~ : Double,
duration_ms? : Double = 220.0,
) -> Unit {
let current = self.offset()
self.smooth_start.set(Some(current))
self.smooth_target.set(Some(Point::new(x~, y~)))
self.smooth_start_time_ms.set(Some(0.0))
self.smooth_duration_ms.set(Some(max_double(0.0, duration_ms)))
}
///|
pub fn ScrollState::tick(self : ScrollState, time_ms : Double) -> Unit {
let target = self.smooth_target.get()
let start = self.smooth_start.get()
let start_time = self.smooth_start_time_ms.get()
let duration = self.smooth_duration_ms.get()
match (target, start, start_time, duration) {
(Some(target), Some(start), Some(start_time), Some(duration)) =>
if duration <= 0.0 {
self.set_offset(target)
self.smooth_target.set(None)
self.smooth_start.set(None)
self.smooth_start_time_ms.set(None)
self.smooth_duration_ms.set(None)
} else {
let progress = clamp_double((time_ms - start_time) / duration, 0.0, 1.0)
let eased = Easing::EaseOut.sample(progress)
let x = start.x + (target.x - start.x) * eased
let y = start.y + (target.y - start.y) * eased
self.set_offset(Point::new(x~, y~))
if progress >= 1.0 {
self.smooth_target.set(None)
self.smooth_start.set(None)
self.smooth_start_time_ms.set(None)
self.smooth_duration_ms.set(None)
} else {
self.smooth_start_time_ms.set(Some(time_ms))
self.smooth_start.set(Some(self.offset()))
}
}
_ => ()
}
}
///|
pub fn ScrollState::is_smooth_scrolling(self : ScrollState) -> Bool {
self.smooth_target.get() is Some(_)
}
///|
pub fn ScrollState::scroll_to(
self : ScrollState,
x~ : Double,
y~ : Double,
) -> Unit {
self.set_offset(Point::new(x~, y~))
}
///|
pub fn FocusState::new(key? : String? = None) -> FocusState {
{ key_slot: State::new(key) }
}
///|
pub fn FocusState::focused_key(self : FocusState) -> String? {
self.key_slot.get()
}
///|
pub fn FocusState::focus(self : FocusState, key : String) -> Unit {
self.key_slot.set(Some(key))
}
///|
pub fn FocusState::clear(self : FocusState) -> Unit {
self.key_slot.set(None)
}
///|
pub fn FocusState::binding(self : FocusState) -> Binding[String?] {
self.key_slot.binding()
}
///|
pub fn NavigationState::new(initial? : String = "root") -> NavigationState {
{ stack_slot: State::new([initial]) }
}
///|
pub fn NavigationState::stack(self : NavigationState) -> Array[String] {
self.stack_slot.get()
}
///|
pub fn NavigationState::current(self : NavigationState) -> String {
let stack = self.stack()
if stack.length() == 0 {
""
} else {
stack[stack.length() - 1]
}
}
///|
pub fn NavigationState::push(self : NavigationState, route : String) -> Unit {
self.stack_slot.update(stack => {
let next = stack.copy()
next.push(route)
next
})
}
///|
pub fn NavigationState::pop(self : NavigationState) -> Bool {
let stack = self.stack()
if stack.length() <= 1 {
false
} else {
let next : Array[String] = []
for index, route in stack {
if index + 1 < stack.length() {
next.push(route)
}
}
self.stack_slot.set(next)
true
}
}
///|
pub fn NavigationState::replace(self : NavigationState, route : String) -> Unit {
self.stack_slot.set([route])
}
///|
pub fn NavigationState::binding(
self : NavigationState,
) -> Binding[Array[String]] {
self.stack_slot.binding()
}
///|
pub fn ComponentContext::watch_scroll(
self : ComponentContext,
state : ScrollState,
) -> Point {
self.watch(state.offset_slot)
}
///|
pub fn ComponentContext::watch_focus(
self : ComponentContext,
state : FocusState,
) -> String? {
self.watch(state.key_slot)
}
///|
pub fn ComponentContext::watch_navigation(
self : ComponentContext,
state : NavigationState,
) -> Array[String] {
self.watch(state.stack_slot)
}