Revamp desktop usability and multiline fields
This commit is contained in:
@@ -12,9 +12,10 @@ use ironstorage::{
|
||||
|
||||
/// Presentation state for editing one storage-owned document.
|
||||
///
|
||||
/// The focused buffer contains one complete pass entry line. Committing that
|
||||
/// buffer delegates classification back to `EntryDocument`, so names, duplicate
|
||||
/// fields, notes, and OTP URI recognition remain storage behavior.
|
||||
/// The focused buffer contains one complete storage-owned logical field.
|
||||
/// Committing it delegates classification back to `EntryDocument`, so names,
|
||||
/// multiline values, duplicate fields, notes, and OTP URI recognition remain
|
||||
/// storage behavior.
|
||||
pub struct EntryEditor {
|
||||
document: EntryDocument,
|
||||
focused: usize,
|
||||
@@ -261,6 +262,22 @@ impl EntryEditor {
|
||||
let Some(field) = self.focused_field() else {
|
||||
return Ok(());
|
||||
};
|
||||
if matches!(
|
||||
field.metadata().kind(),
|
||||
EntryFieldKind::Username
|
||||
| EntryFieldKind::Email
|
||||
| EntryFieldKind::Url
|
||||
| EntryFieldKind::Field
|
||||
| EntryFieldKind::Note
|
||||
) {
|
||||
let mut replacement = Vec::with_capacity(self.buffer.expose().len() + 1);
|
||||
replacement.extend_from_slice(&self.buffer.expose()[..self.cursor]);
|
||||
replacement.push(b'\n');
|
||||
replacement.extend_from_slice(&self.buffer.expose()[self.cursor..]);
|
||||
self.cursor += 1;
|
||||
self.replace_buffer(replacement);
|
||||
return Ok(());
|
||||
}
|
||||
let id = field.id();
|
||||
let left = self.buffer.expose()[..self.cursor].to_vec();
|
||||
let right = self.buffer.expose()[self.cursor..].to_vec();
|
||||
@@ -281,8 +298,10 @@ impl EntryEditor {
|
||||
return Ok(());
|
||||
}
|
||||
let id = field.id();
|
||||
self.document
|
||||
.update(id, EntryFieldDraft::line(self.buffer.expose().to_vec())?)?;
|
||||
self.document.update(
|
||||
id,
|
||||
EntryFieldDraft::multiline(self.buffer.expose().to_vec()),
|
||||
)?;
|
||||
self.dirty = true;
|
||||
Ok(())
|
||||
}
|
||||
@@ -303,8 +322,7 @@ impl EntryEditor {
|
||||
let Some(id) = self.focused_field().map(EntryField::id) else {
|
||||
return;
|
||||
};
|
||||
let draft = EntryFieldDraft::line(self.buffer.expose().to_vec())
|
||||
.expect("the single-line editor never inserts line endings into its buffer");
|
||||
let draft = EntryFieldDraft::multiline(self.buffer.expose().to_vec());
|
||||
self.document
|
||||
.update(id, draft)
|
||||
.expect("the focused field identifier belongs to the document");
|
||||
@@ -376,7 +394,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_split_remove_and_reorder_keep_stable_focus() {
|
||||
fn add_multiline_remove_and_reorder_keep_stable_focus() {
|
||||
let mut editor = EntryEditor::new(fixture_document("email/personal"));
|
||||
let initial = editor.document().fields().len();
|
||||
editor.add_after_focused().expect("add");
|
||||
@@ -385,11 +403,15 @@ mod tests {
|
||||
editor.insert_character('b');
|
||||
editor.move_cursor_left();
|
||||
editor.split_line().expect("split");
|
||||
assert_eq!(editor.document().fields().len(), initial + 2);
|
||||
editor.move_focused_up().expect("move up");
|
||||
assert_eq!(editor.focused_index(), Some(1));
|
||||
editor.remove_focused().expect("remove");
|
||||
assert_eq!(editor.document().fields().len(), initial + 1);
|
||||
assert_eq!(
|
||||
editor.focused_contents(editor.focused_field().expect("field").id()),
|
||||
Some(b"a\nb".as_slice())
|
||||
);
|
||||
editor.move_focused_up().expect("move up");
|
||||
assert_eq!(editor.focused_index(), Some(0));
|
||||
editor.remove_focused().expect("remove");
|
||||
assert_eq!(editor.document().fields().len(), initial);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user