use crate::{Context, Node, ui::event::EventResult, windowing::WindowDimension};
pub mod adapter;
pub mod boxed;
pub mod either;
pub mod event;
pub mod focus;
pub mod keyed;
pub mod kinetic;
pub mod layer;
pub mod lens;
pub mod memoize;
pub mod morph;
pub mod presence;
pub mod router;
pub mod style;
pub mod transition;
pub mod widgets;
pub use crate::switch;
pub use adapter::{ViewAdaptExt, adapt};
pub use boxed::{BoxedElement, BoxedView, BoxedViewExt, boxed};
pub use either::{Either, ViewEitherExt, either};
pub use event::{
DragContext, DragElement, DragHandler, DragPhase, EventKind, KeyActionKind, KeyEventContext,
KeyHandler, KeyScope, ScrollContext, ScrollHandler, ScrollSource, ThumbScrollContext,
ThumbScrollHandler, TickHandler, ViewEventExt,
};
pub use focus::{Focusable, FocusableExt};
pub use keyed::{Keyed, KeyedViewSequence, keyed, keyed_sequence};
pub use kinetic::KineticTracker;
pub use layer::{Layer, ViewLayerExt, layer};
pub use lens::Lens;
pub use morph::{MorphId, MorphTransition, MorphViewExt, Morphable, morphable};
pub use presence::{Presence, PresenceViewExt, presence};
pub use router::{Router, router};
pub use style::ViewStyleExt;
pub use transition::{Motion, MotionOffset, PageTransition, Transition, TransitionOrder};
#[derive(Clone, Debug)]
pub enum Event {
CursorMoved {
x: f32,
y: f32,
delta_x: f32,
delta_y: f32,
hit_nodes: Vec<Node>,
},
MouseInput {
button: winit::event::MouseButton,
pressed: bool,
x: f32,
y: f32,
hit_nodes: Vec<Node>,
},
StylusInput {
x: f32,
y: f32,
pressure: f32,
tilt: Option<(f32, f32)>,
is_eraser: bool,
pressed: bool,
hit_nodes: Vec<Node>,
},
MouseWheel {
delta_x: f32,
delta_y: f32,
is_touchpad: bool,
phase: winit::event::TouchPhase,
hit_nodes: Vec<Node>,
},
KeyboardInput {
event: KeyEvent,
is_synthetic: bool,
},
Ime(winit::event::Ime),
Tick {
dt: f32,
},
WindowResized(WindowDimension),
ThumbScroll {
node: Node,
context: ThumbScrollContext,
},
Scroll {
node: Node,
context: ScrollContext,
},
FocusLost {
node: Node,
},
Action {
node: Node,
action: accesskit::Action,
data: Option<accesskit::ActionData>,
},
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct KeyEvent {
pub physical_key: winit::keyboard::PhysicalKey,
pub logical_key: winit::keyboard::Key,
pub text: Option<winit::keyboard::SmolStr>,
pub location: winit::keyboard::KeyLocation,
pub state: winit::event::ElementState,
pub repeat: bool,
}
impl KeyEvent {
pub fn new(logical_key: winit::keyboard::Key, state: winit::event::ElementState) -> Self {
Self {
physical_key: winit::keyboard::PhysicalKey::Unidentified(
winit::keyboard::NativeKeyCode::Unidentified,
),
logical_key,
text: None,
location: winit::keyboard::KeyLocation::Standard,
state,
repeat: false,
}
}
}
impl From<winit::event::KeyEvent> for KeyEvent {
fn from(ev: winit::event::KeyEvent) -> Self {
Self {
physical_key: ev.physical_key,
logical_key: ev.logical_key,
text: ev.text,
location: ev.location,
state: ev.state,
repeat: ev.repeat,
}
}
}
pub trait View<State> {
type Element;
type Message;
fn build(&self, ctx: &mut Context) -> Self::Element;
fn rebuild(&self, prev: &Self, ctx: &mut Context, element: &mut Self::Element);
fn rebuild_with_parent(
&self,
prev: &Self,
ctx: &mut Context,
element: &mut Self::Element,
_parent: Node,
_next_sibling: Option<Node>,
) {
self.rebuild(prev, ctx, element);
}
fn teardown(&self, ctx: &mut Context, element: &mut Self::Element);
fn get_node(&self, element: &Self::Element) -> Node;
fn handle_event(
&self,
element: &mut Self::Element,
state: &State,
event: Event,
ctx: &mut Context,
) -> (EventResult, Option<Self::Message>);
}
pub trait ViewSequence<State> {
type Elements;
type Message;
fn build(&self, ctx: &mut Context, parent: Node) -> Self::Elements;
fn rebuild(&self, prev: &Self, ctx: &mut Context, elements: &mut Self::Elements, parent: Node);
fn teardown(&self, ctx: &mut Context, elements: &mut Self::Elements);
fn handle_event(
&self,
elements: &mut Self::Elements,
state: &State,
event: Event,
ctx: &mut Context,
) -> (EventResult, Option<Self::Message>);
}
macro_rules! impl_view_tuple {
( $($idx:tt => $t:ident),* ) => {
impl<State, Msg, $($t),*> ViewSequence<State> for ($($t,)*)
where
$($t: View<State, Message = Msg>),*
{
type Elements = ($($t::Element,)*);
type Message = Msg;
fn build(&self, ctx: &mut Context, parent: Node) -> Self::Elements {
(
$({
let child_element = self.$idx.build(ctx);
parent.append(ctx, self.$idx.get_node(&child_element));
child_element
},)*
)
}
fn rebuild(&self, prev: &Self, ctx: &mut Context, elements: &mut Self::Elements, parent: Node) {
let mut nodes = [
$(self.$idx.get_node(&elements.$idx),)*
];
#[allow(unused_assignments)]
{
$(
let next_sibling = nodes[($idx + 1)..].iter().copied().find(|n| n.is_valid());
self.$idx.rebuild_with_parent(&prev.$idx, ctx, &mut elements.$idx, parent, next_sibling);
nodes[$idx] = self.$idx.get_node(&elements.$idx);
)*
}
}
fn teardown(&self, ctx: &mut Context, elements: &mut Self::Elements) {
$(
self.$idx.teardown(ctx, &mut elements.$idx);
)*
}
fn handle_event(
&self,
elements: &mut Self::Elements,
state: &State,
event: Event,
ctx: &mut Context,
) -> (EventResult, Option<Self::Message>) {
let is_tick = matches!(event, Event::Tick { .. });
let mut handled = EventResult::Ignored;
let mut emitted_msg = None;
$(
if (is_tick || handled == EventResult::Ignored) && emitted_msg.is_none() {
let (res, msg) = self.$idx.handle_event(
&mut elements.$idx,
state,
event.clone(),
ctx
);
handled = handled.or(res);
if msg.is_some() {
emitted_msg = msg;
}
}
)*
(handled, emitted_msg)
}
}
};
}
impl_view_tuple!(0 => A);
impl_view_tuple!(0 => A, 1 => B);
impl_view_tuple!(0 => A, 1 => B, 2 => C);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J, 10 => K);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J, 10 => K, 11 => L);
impl_view_tuple!(0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H, 8 => I, 9 => J, 10 => K, 11 => L, 12 => M);
impl<State, Msg, V> ViewSequence<State> for Vec<V>
where
V: View<State, Message = Msg>,
{
type Elements = Vec<V::Element>;
type Message = Msg;
fn build(&self, ctx: &mut Context, parent: Node) -> Self::Elements {
let mut elements = Vec::with_capacity(self.len());
for view in self {
let el = view.build(ctx);
parent.append(ctx, view.get_node(&el));
elements.push(el);
}
elements
}
fn rebuild(&self, prev: &Self, ctx: &mut Context, elements: &mut Self::Elements, parent: Node) {
let min_len = self.len().min(prev.len());
for i in 0..min_len {
self[i].rebuild(&prev[i], ctx, &mut elements[i]);
}
for i in min_len..self.len() {
let el = self[i].build(ctx);
parent.append(ctx, self[i].get_node(&el));
elements.push(el);
}
if self.len() < prev.len() {
for i in min_len..prev.len() {
prev[i].teardown(ctx, &mut elements[i]);
}
elements.truncate(self.len());
}
}
fn teardown(&self, ctx: &mut Context, elements: &mut Self::Elements) {
for (view, el) in self.iter().zip(elements.iter_mut()) {
view.teardown(ctx, el);
}
}
fn handle_event(
&self,
elements: &mut Self::Elements,
state: &State,
event: Event,
ctx: &mut Context,
) -> (EventResult, Option<Self::Message>) {
let is_tick = matches!(event, Event::Tick { .. });
let mut handled = EventResult::Ignored;
let mut emitted_msg = None;
for (v, el) in self.iter().zip(elements.iter_mut()) {
if (is_tick || handled == EventResult::Ignored) && emitted_msg.is_none() {
let (res, msg) = v.handle_event(el, state, event.clone(), ctx);
handled = handled.or(res);
if msg.is_some() {
emitted_msg = msg;
}
}
}
(handled, emitted_msg)
}
}
impl<State, V> View<State> for Option<V>
where
V: View<State>,
{
type Element = Option<V::Element>;
type Message = V::Message;
fn build(&self, ctx: &mut Context) -> Self::Element {
self.as_ref().map(|v| v.build(ctx))
}
fn rebuild_with_parent(
&self,
prev: &Self,
ctx: &mut Context,
element: &mut Self::Element,
parent: Node,
next_sibling: Option<Node>,
) {
match (self, prev, element) {
(Some(new_view), Some(old_view), Some(el)) => {
new_view.rebuild_with_parent(old_view, ctx, el, parent, next_sibling);
}
(Some(new_view), _, el_slot @ None) => {
let el = new_view.build(ctx);
let node = new_view.get_node(&el);
if let Some(sibling) = next_sibling {
node.put_before(ctx, sibling);
} else {
parent.append(ctx, node);
}
*el_slot = Some(el);
}
(None, Some(old_view), el_slot @ Some(_)) => {
if let Some(mut el) = el_slot.take() {
let node = old_view.get_node(&el);
node.remove(ctx);
old_view.teardown(ctx, &mut el);
}
}
_ => {}
}
}
fn rebuild(&self, prev: &Self, ctx: &mut Context, element: &mut Self::Element) {
match (self, prev, element) {
(Some(new_view), Some(old_view), Some(el)) => {
new_view.rebuild(old_view, ctx, el);
}
(Some(new_view), _, el_slot @ None) => {
*el_slot = Some(new_view.build(ctx));
}
(None, Some(old_view), el_slot @ Some(_)) => {
if let Some(mut el) = el_slot.take() {
let node = old_view.get_node(&el);
node.remove(ctx);
old_view.teardown(ctx, &mut el);
}
}
_ => {}
}
}
fn teardown(&self, ctx: &mut Context, element: &mut Self::Element) {
if let (Some(view), Some(el)) = (self.as_ref(), element) {
view.teardown(ctx, el);
}
}
fn get_node(&self, element: &Self::Element) -> Node {
if let (Some(view), Some(el)) = (self.as_ref(), element) {
view.get_node(el)
} else {
Node::get_invalid()
}
}
fn handle_event(
&self,
element: &mut Self::Element,
state: &State,
event: Event,
ctx: &mut Context,
) -> (EventResult, Option<Self::Message>) {
if let (Some(view), Some(el)) = (self.as_ref(), element) {
view.handle_event(el, state, event, ctx)
} else {
(EventResult::Ignored, None)
}
}
}
impl<State, V> ViewSequence<State> for Option<V>
where
V: View<State>,
{
type Elements = Option<V::Element>;
type Message = V::Message;
fn build(&self, ctx: &mut Context, parent: Node) -> Self::Elements {
if let Some(view) = self {
let el = view.build(ctx);
parent.append(ctx, view.get_node(&el));
Some(el)
} else {
None
}
}
fn rebuild(&self, prev: &Self, ctx: &mut Context, elements: &mut Self::Elements, parent: Node) {
match (self, prev, elements) {
(Some(new_view), Some(old_view), Some(el)) => {
new_view.rebuild_with_parent(old_view, ctx, el, parent, None);
}
(Some(new_view), _, el_slot @ None) => {
let el = new_view.build(ctx);
parent.append(ctx, new_view.get_node(&el));
*el_slot = Some(el);
}
(None, Some(old_view), el_slot @ Some(_)) => {
if let Some(mut el) = el_slot.take() {
let node = old_view.get_node(&el);
node.remove(ctx);
old_view.teardown(ctx, &mut el);
}
}
_ => {}
}
}
fn teardown(&self, ctx: &mut Context, elements: &mut Self::Elements) {
if let (Some(view), Some(el)) = (self, elements) {
view.teardown(ctx, el);
}
}
fn handle_event(
&self,
elements: &mut Self::Elements,
state: &State,
event: Event,
ctx: &mut Context,
) -> (EventResult, Option<Self::Message>) {
if let (Some(view), Some(el)) = (self, elements) {
view.handle_event(el, state, event, ctx)
} else {
(EventResult::Ignored, None)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ui::widgets::{button, row};
#[test]
fn test_vec_view_sequence_grow_and_shrink() {
let mut ctx = Context::new();
let list5: Vec<_> = (0..5).map(|i| button::<_, ()>(format!("{i}"))).collect();
let list6: Vec<_> = (0..6).map(|i| button::<_, ()>(format!("{i}"))).collect();
let row_view5 = row(list5);
let row_view6 = row(list6);
let mut el = View::<()>::build(&row_view5, &mut ctx);
View::<()>::rebuild(&row_view6, &row_view5, &mut ctx, &mut el);
View::<()>::teardown(&row_view6, &mut ctx, &mut el);
}
#[test]
fn test_option_view_sequence_sibling_ordering() {
use crate::ui::widgets::{Text, column, text};
type OptText = Option<Text<()>>;
let mut ctx = Context::new();
let view_1 = column((
text::<_, ()>("BANNER"),
Some(text::<_, ()>("music_dir")),
None as OptText,
None as OptText,
text::<_, ()>("log"),
));
let mut el = View::<()>::build(&view_1, &mut ctx);
let parent_node = View::<()>::get_node(&view_1, &el);
let initial_children = parent_node.children(&ctx);
assert_eq!(initial_children.len(), 3);
let banner_node = initial_children[0];
let music_node = initial_children[1];
let log_node = initial_children[2];
let view_2 = column((
text::<_, ()>("BANNER"),
None as OptText,
Some(text::<_, ()>("progress_bar")),
None as OptText,
text::<_, ()>("log"),
));
View::<()>::rebuild(&view_2, &view_1, &mut ctx, &mut el);
let updated_children = parent_node.children(&ctx);
assert_eq!(updated_children.len(), 3);
assert_eq!(updated_children[0], banner_node);
let progress_node = updated_children[1];
assert_ne!(progress_node, music_node);
assert_eq!(updated_children[2], log_node);
View::<()>::teardown(&view_2, &mut ctx, &mut el);
}
}