use polyhorn_core::CommandBuffer;
use polyhorn_ios_sys::coregraphics::CGRect;
use polyhorn_ios_sys::foundation::NSNumber;
use polyhorn_ios_sys::polykit::{PLYCallback, PLYKeyboardAvoidingView, PLYView};
use polyhorn_ui::geometry::{ByEdge, Dimension};
use polyhorn_ui::layout::LayoutAxisY;
use polyhorn_ui::styles::{Position, Relative, ViewStyle};
use std::sync::Arc;
use crate::prelude::*;
use crate::raw::{Builtin, Container, ContainerID, OpaqueContainer};
use crate::{Key, Reference};
#[derive(Default)]
pub struct LayoutAdjustment {
margin: ByEdge<Dimension<f32>>,
}
impl LayoutAdjustment {
pub fn new() -> LayoutAdjustment {
Default::default()
}
pub fn margin_bottom(self, bottom: Dimension<f32>) -> LayoutAdjustment {
LayoutAdjustment {
margin: ByEdge {
vertical: LayoutAxisY {
bottom,
..self.margin.vertical
},
..self.margin
},
..self
}
}
}
#[derive(Default)]
pub struct KeyboardAvoidingView {
pub transform: Option<Arc<dyn Fn(f32) -> LayoutAdjustment + Send + Sync>>,
}
impl Container for PLYKeyboardAvoidingView {
fn mount(&mut self, child: &mut OpaqueContainer) {
if let Some(view) = child.container().to_view() {
PLYKeyboardAvoidingView::to_view(self).add_subview(&view)
}
}
fn unmount(&mut self) {
PLYKeyboardAvoidingView::to_view(self).remove_from_superview();
}
fn to_view(&self) -> Option<PLYView> {
Some(PLYKeyboardAvoidingView::to_view(self))
}
}
impl Component for KeyboardAvoidingView {
fn render(&self, manager: &mut Manager) -> Element {
let view_ref: Reference<Option<ContainerID>> = use_reference!(manager, None);
let transform = self.transform.clone();
use_layout_effect!(manager, move |link, buffer| {
let id = match view_ref.apply(link, |id| id.to_owned()) {
Some(id) => id,
None => return,
};
buffer.mutate(&[id], move |containers, _| {
let container = &mut containers[0];
let layout = match container.layout() {
Some(layout) => layout.clone(),
None => return,
};
if let Some(view) = container.downcast_mut::<PLYKeyboardAvoidingView>() {
{
let layout = layout.clone();
view.set_on_keyboard(PLYCallback::new(move |height: NSNumber| {
if let Some(transform) = transform.as_ref() {
let adjustment = transform(height.float_value());
layout.set_style(ViewStyle {
position: Position::Relative(Relative {
flex_grow: 1.0,
..Default::default()
}),
margin: ByEdge {
vertical: LayoutAxisY {
bottom: adjustment.margin.vertical.bottom,
..Default::default()
},
..Default::default()
},
..Default::default()
});
layout.layouter().write().unwrap().recompute_roots();
}
}));
}
view.to_view().set_layout(move || {
let current = layout.current();
CGRect::new(
current.origin.x as _,
current.origin.y as _,
current.size.width as _,
current.size.height as _,
)
});
}
});
});
Element::builtin(
Key::new(()),
Builtin::KeyboardAvoidingView,
manager.children(),
Some(view_ref.weak(manager)),
)
}
}