From 0b62a2657a9deb5fa4a562d6ec9bcdddb697dd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gianguido=20Sor=C3=A0?= Date: Thu, 31 Mar 2022 19:24:53 +0200 Subject: [PATCH] feature: generalization of modals.get_text() Added a get_text_multi() method on modals which builds, shows and returns text taken from a modal view with a given amount of fields. Calling get_text() is equal to a call to get_text_multi() with fields = 1. --- services/gam/src/modal.rs | 2 +- services/gam/src/modal/textentry.rs | 53 ++++++++++++++++++++++++----- services/modals/src/api.rs | 2 ++ services/modals/src/lib.rs | 19 +++++++++-- services/modals/src/main.rs | 17 ++++++--- services/shellchat/src/cmds/ver.rs | 2 +- 6 files changed, 77 insertions(+), 18 deletions(-) diff --git a/services/gam/src/modal.rs b/services/gam/src/modal.rs index 4e5800228..d00bb7705 100644 --- a/services/gam/src/modal.rs +++ b/services/gam/src/modal.rs @@ -70,7 +70,7 @@ impl ItemName { } } -#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone, Eq, PartialEq, Default)] pub struct TextEntryPayload { pub id: u32, pub content: String::<256> diff --git a/services/gam/src/modal/textentry.rs b/services/gam/src/modal/textentry.rs index 7074cef68..e29a8b18a 100644 --- a/services/gam/src/modal/textentry.rs +++ b/services/gam/src/modal/textentry.rs @@ -12,6 +12,15 @@ const MAX_FIELDS: i16 = 10; pub type ValidatorErr = xous_ipc::String::<256>; +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone, Eq, PartialEq, Default)] +pub struct TextEntryPayloads ([TextEntryPayload; MAX_FIELDS as usize]); + +impl TextEntryPayloads { + pub fn first(&self) -> TextEntryPayload { + self.0[0] + } +} + #[derive(Debug, Copy, Clone, num_derive::FromPrimitive, num_derive::ToPrimitive)] pub enum TextEntryVisibility { /// text is fully visible @@ -33,6 +42,7 @@ pub struct TextEntry { pub validator: Option Option>, pub action_payloads: Vec, + max_field_amount: u32, selected_field: i16, } @@ -46,6 +56,7 @@ impl Default for TextEntry { validator: Default::default(), selected_field: Default::default(), action_payloads: Default::default(), + max_field_amount: 0, } } } @@ -75,6 +86,11 @@ impl TextEntry { ..Default::default() } } + + pub fn reset_action_payloads(&mut self, fields: u32) { + self.action_payloads = vec![TextEntryPayload::default(); fields as usize]; + self.max_field_amount = fields; + } } @@ -99,7 +115,7 @@ impl ActionApi for TextEntry { let glyph_height = match self.action_payloads.len() { 0 | 1 => 1, - _ => glyph_height * self.action_payloads.len() as i16 + 2, + _ => glyph_height * (self.action_payloads.len() as i16 + 2), } as i16; if self.is_password { @@ -116,13 +132,21 @@ impl ActionApi for TextEntry { PixelColor::Dark }; - for payload in self.action_payloads.clone() { + let mut current_height = at_height; + let payloads = self.action_payloads.clone(); + + for (index, payload) in payloads.iter().enumerate() { + current_height = match index { + 0 => at_height, + _ => current_height + at_height, + }; + // draw the currently entered text let mut tv = TextView::new( modal.canvas, TextBounds::BoundingBox(Rectangle::new( - Point::new(modal.margin, at_height), - Point::new(modal.canvas_width - modal.margin, at_height + modal.line_height)) + Point::new(modal.margin, current_height), + Point::new(modal.canvas_width - modal.margin, current_height + modal.line_height)) )); tv.ellipsis = true; tv.invert = self.is_password; @@ -266,15 +290,19 @@ impl ActionApi for TextEntry { // draw a line for where text gets entered (don't use a box, fitting could be awkward) modal.gam.draw_line(modal.canvas, Line::new_with_style( - Point::new(modal.margin, at_height + modal.line_height + 4), - Point::new(modal.canvas_width - modal.margin, at_height + modal.line_height + 4), + Point::new(modal.margin, current_height + modal.line_height + 4), + Point::new(modal.canvas_width - modal.margin, current_height + modal.line_height + 4), DrawStyle::new(color, color, 1)) ).expect("couldn't draw entry line"); + } } fn key_action(&mut self, k: char) -> (Option, bool) { let mut payload = self.action_payloads[self.selected_field as usize]; + let can_move_downwards = !(self.selected_field+1 == self.max_field_amount as i16); + let can_move_upwards = !(self.selected_field-1 < 0); + log::trace!("key_action: {}", k); match k { '←' => { @@ -311,16 +339,23 @@ impl ActionApi for TextEntry { } } - let buf = Buffer::into_buf(payload).expect("couldn't convert message to payload"); + let mut payloads: TextEntryPayloads = Default::default(); + payloads.0[..self.max_field_amount as usize].copy_from_slice(&self.action_payloads[..self.max_field_amount as usize]); + let buf = Buffer::into_buf(payloads).expect("couldn't convert message to payload"); buf.send(self.action_conn, self.action_opcode).map(|_| ()).expect("couldn't send action message"); payload.volatile_clear(); // ensure the local copy of text is zero'd out + // TODO: zero out EVERYTHING here! return (None, true) } '↑' => { - self.selected_field -= 1 + if can_move_upwards { + self.selected_field -= 1 + } } '↓' => { - self.selected_field += 1 + if can_move_downwards { + self.selected_field += 1 + } } '\u{0}' => { // ignore null messages diff --git a/services/modals/src/api.rs b/services/modals/src/api.rs index 3838b9d51..acaaf8aea 100644 --- a/services/modals/src/api.rs +++ b/services/modals/src/api.rs @@ -31,6 +31,8 @@ pub struct ManagedPromptWithTextResponse { pub validator: Option<[u32; 4]>, /// the opcode to pass the validator pub validator_op: u32, + /// the amount of fields to read + pub fields: u32, } #[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Copy, Clone)] pub struct ManagedNotification { diff --git a/services/modals/src/lib.rs b/services/modals/src/lib.rs index 325fc7e92..119a61f7d 100644 --- a/services/modals/src/lib.rs +++ b/services/modals/src/lib.rs @@ -33,6 +33,20 @@ impl Modals { maybe_validator: Option Option>, maybe_validator_op: Option, ) -> Result { + match self.get_text_multi(prompt, maybe_validator, maybe_validator_op, 1) { + Ok(res) => { + return Ok(res.first()) + }, + Err(error) => return Err(error), + } + } + + pub fn get_text_multi(&self, + prompt: &str, + maybe_validator: Option Option>, + maybe_validator_op: Option, + fields: u32, + ) -> Result { self.lock(); let validator = if let Some(validator) = maybe_validator { // create a one-time use server @@ -70,7 +84,8 @@ impl Modals { token: self.token, prompt: xous_ipc::String::from_str(prompt), validator, - validator_op: maybe_validator_op.unwrap_or(0) + validator_op: maybe_validator_op.unwrap_or(0), + fields: fields, }; let mut buf = Buffer::into_buf(spec).or(Err(xous::Error::InternalError))?; buf.lend_mut(self.conn, Opcode::PromptWithTextResponse.to_u32().unwrap()).or(Err(xous::Error::InternalError))?; @@ -80,7 +95,7 @@ impl Modals { unsafe{xous::disconnect(cid).unwrap()}; // must disconnect before destroy to avoid the CID from hanging out in our outbound table which is limited to 32 entries xous::destroy_server(SID::from_array(server)).expect("couldn't destroy temporary server"); } - match buf.to_original::() { + match buf.to_original::() { Ok(response) => Ok(response), _ => Err(xous::Error::InternalError) } diff --git a/services/modals/src/main.rs b/services/modals/src/main.rs index 7a5894c53..0c0911c80 100644 --- a/services/modals/src/main.rs +++ b/services/modals/src/main.rs @@ -34,7 +34,7 @@ enum RendererState { RunProgress(ManagedProgress), RunNotification(ManagedNotification), /// response ready state - ResponseText(TextEntryPayload), + ResponseText(TextEntryPayloads), ResponseRadio(ItemName), ResponseCheckBox(CheckBoxPayload), RunDynamicNotification(DynamicNotification), @@ -142,8 +142,14 @@ fn xmain() -> ! { log::debug!("initiating text entry modal"); #[cfg(feature="tts")] tts.tts_simple(config.prompt.as_str().unwrap()).unwrap(); + renderer_modal.modify( - Some(ActionType::TextEntry(text_action.clone())), + Some(ActionType::TextEntry({ + let mut ta = text_action.clone(); + ta.reset_action_payloads(config.fields); + + ta + })), Some(config.prompt.as_str().unwrap()), false, None, true, None ); @@ -310,9 +316,10 @@ fn xmain() -> ! { RendererState::RunText(config) => { log::trace!("validating text entry modal"); let buf = unsafe { Buffer::from_memory_message(msg.body.memory_message().unwrap()) }; - let text = buf.to_original::().unwrap(); + let text = buf.to_original::().unwrap(); + // TODO: figure out how to associate validation with id's. if let Some(validator_sid) = config.validator { - let cid = xous::connect(SID::from_array(validator_sid)).unwrap(); + /*let cid = xous::connect(SID::from_array(validator_sid)).unwrap(); let validation = Validation { text, opcode: config.validator_op, @@ -332,7 +339,7 @@ fn xmain() -> ! { } else { // the change in mutex_op enum type will signal the state change to the caller *mutex_op = RendererState::ResponseText(text); - } + }*/ } else { *mutex_op = RendererState::ResponseText(text); } diff --git a/services/shellchat/src/cmds/ver.rs b/services/shellchat/src/cmds/ver.rs index 158badbb4..7f6310be6 100644 --- a/services/shellchat/src/cmds/ver.rs +++ b/services/shellchat/src/cmds/ver.rs @@ -8,7 +8,7 @@ pub struct Ver { // TODO: remove me impl Ver { fn modal(&self, env: &mut CommonEnv) { - log::info!("modal data: {:?}", env.modals.get_text("Get Text", None, None)) + log::info!("modal data: {:#?}", env.modals.get_text_multi("Get Text", None, None, 4)) } }