use core::convert::{TryFrom, TryInto}; use crate::{ error::Error, micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr}, ui::{ component::{Child, Text}, display, layout::obj::LayoutObj, }, util, }; use super::{ component::{Button, Dialog, DialogMsg}, 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::LeftClicked => 1.try_into(), DialogMsg::RightClicked => 2.try_into(), } } } #[no_mangle] extern "C" fn ui_layout_new_confirm_action( n_args: usize, args: *const Obj, kwargs: *const Map, ) -> Obj { let block = |_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 verb_cancel: Option = kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into_option()?; let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?; let format = match (&action, &description, reverse) { (Some(_), Some(_), false) => "{bold}{action}\n\r{normal}{description}", (Some(_), Some(_), true) => "{normal}{description}\n\r{bold}{action}", (Some(_), None, _) => "{bold}{action}", (None, Some(_), _) => "{normal}{description}", _ => "", }; let left = verb_cancel .map(|label| |area, pos| Button::with_text(area, pos, label, theme::button_cancel())); let right = verb .map(|label| |area, pos| Button::with_text(area, pos, label, theme::button_default())); let obj = LayoutObj::new(Child::new(Dialog::new( display::screen(), |area| { Text::new::(area, format) .with(b"action", action.unwrap_or("".into())) .with(b"description", description.unwrap_or("".into())) }, left, right, Some(title), )))?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } #[cfg(test)] mod tests { use crate::trace::{Trace, Tracer}; use super::*; impl Tracer for Vec { fn bytes(&mut self, b: &[u8]) { self.extend(b) } fn string(&mut self, s: &str) { self.extend(s.as_bytes()) } fn symbol(&mut self, name: &str) { self.extend(name.as_bytes()) } fn open(&mut self, name: &str) { self.extend(b"<"); self.extend(name.as_bytes()); self.extend(b" "); } fn field(&mut self, name: &str, value: &dyn Trace) { self.extend(name.as_bytes()); self.extend(b":"); value.trace(self); self.extend(b" "); } fn close(&mut self) { self.extend(b">") } } 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| { Text::new( area, "Testing text layout, with some text, and some more text. And {param}", ) .with(b"param", b"parameters!") }, Some(|area, pos| Button::with_text(area, pos, "Left", theme::button_cancel())), Some(|area, pos| Button::with_text(area, pos, "Right", theme::button_default())), None, )); assert_eq!( trace(&layout), r#" left: