use core::convert::{TryFrom, TryInto}; use crate::{ error::Error, micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr}, ui::{ component::{base::ComponentExt, text::paragraphs::Paragraphs, Child, FormattedText}, display, layout::obj::LayoutObj, }, util, }; use super::{ component::{Button, ButtonMsg, DialogMsg, Frame, HoldToConfirm, HoldToConfirmMsg, SwipePage}, theme, }; impl TryFrom> for Obj where Obj: TryFrom, Error: From<>::Error>, { type Error = Error; fn try_from(val: DialogMsg) -> Result { match val { DialogMsg::Content(c) => Ok(c.try_into()?), DialogMsg::Left(ButtonMsg::Clicked) => 1.try_into(), DialogMsg::Right(ButtonMsg::Clicked) => 2.try_into(), _ => Ok(Obj::const_none()), } } } impl TryFrom> for Obj where Obj: TryFrom, Error: From<>::Error>, { type Error = Error; fn try_from(val: HoldToConfirmMsg) -> Result { match val { HoldToConfirmMsg::Content(c) => Ok(c.try_into()?), HoldToConfirmMsg::Confirmed => 1.try_into(), HoldToConfirmMsg::Cancelled => 2.try_into(), } } } #[no_mangle] extern "C" fn ui_layout_new_example(_param: Obj) -> Obj { let block = move || { let layout = LayoutObj::new(HoldToConfirm::new(display::screen(), |area| { FormattedText::new::( area, "Testing text layout, with some text, and some more text. And {param}", ) .with(b"param", b"parameters!") }))?; Ok(layout.into()) }; unsafe { util::try_or_raise(block) } } #[no_mangle] extern "C" fn ui_layout_new_confirm_action( n_args: usize, args: *const Obj, kwargs: *const Map, ) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let action: Option = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?; let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; let verb: Option = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?; let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?; let obj = LayoutObj::new( Frame::new(theme::borders(), title, |area| { SwipePage::new( area, theme::BG, |area| { let action = action.unwrap_or("".into()); let description = description.unwrap_or("".into()); let mut para = Paragraphs::new(area); if !reverse { para = para .add::(theme::FONT_BOLD, action) .add::(theme::FONT_NORMAL, description); } else { para = para .add::(theme::FONT_NORMAL, description) .add::(theme::FONT_BOLD, action); } para }, |area| { Button::array2( area, |area| Button::with_icon(area, theme::ICON_CANCEL), |msg| (matches!(msg, ButtonMsg::Clicked)).then(|| false), |area| { Button::with_text(area, verb.unwrap_or("CONFIRM".into())) .styled(theme::button_confirm()) }, |msg| (matches!(msg, ButtonMsg::Clicked)).then(|| true), ) }, ) }) .into_child(), )?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } #[cfg(test)] mod tests { use crate::{ trace::Trace, ui::model_tt::component::{Button, Dialog}, }; use super::*; fn trace(val: &impl Trace) -> String { let mut t = Vec::new(); val.trace(&mut t); String::from_utf8(t).unwrap() } #[test] fn trace_example_layout() { let layout = Child::new(Dialog::new( display::screen(), |area| { FormattedText::new::( area, "Testing text layout, with some text, and some more text. And {param}", ) .with(b"param", b"parameters!") }, |area| Button::with_text(area, b"Left"), |area| Button::with_text(area, b"Right"), )); assert_eq!( trace(&layout), " left: