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.
This commit is contained in:
Gianguido Sorà 2022-03-31 19:24:53 +02:00
parent 83e61f5f1b
commit 0b62a2657a
6 changed files with 77 additions and 18 deletions

View file

@ -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>

View file

@ -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<fn(TextEntryPayload, u32) -> Option<ValidatorErr>>,
pub action_payloads: Vec<TextEntryPayload>,
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<ValidatorErr>, 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

View file

@ -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 {

View file

@ -33,6 +33,20 @@ impl Modals {
maybe_validator: Option<fn(TextEntryPayload, u32) -> Option<ValidatorErr>>,
maybe_validator_op: Option<u32>,
) -> Result<TextEntryPayload, xous::Error> {
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<fn(TextEntryPayload, u32) -> Option<ValidatorErr>>,
maybe_validator_op: Option<u32>,
fields: u32,
) -> Result<TextEntryPayloads, xous::Error> {
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::<TextEntryPayload, _>() {
match buf.to_original::<TextEntryPayloads, _>() {
Ok(response) => Ok(response),
_ => Err(xous::Error::InternalError)
}

View file

@ -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::<gam::modal::TextEntryPayload, _>().unwrap();
let text = buf.to_original::<gam::modal::TextEntryPayloads, _>().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);
}

View file

@ -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))
}
}