lib: Use a buffered value for text edits in imUI

This commit is contained in:
Erik Kundt
2024-09-04 14:37:38 +02:00
parent 9bceebd1a6
commit 41d240b221
+131 -28
View File
@@ -1013,12 +1013,13 @@ pub mod widget {
// Put all characters together except the selected one.
// By leaving the selected one out, it is forgotten and therefore deleted.
self.text = before_char_to_delete.chain(after_char_to_delete).collect();
self.move_cursor_left();
}
}
fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
new_cursor_pos.clamp(0, self.text.clone().len())
new_cursor_pos.clamp(0, self.text.len())
}
}
@@ -1082,7 +1083,7 @@ pub mod widget {
let layout = Layout::vertical(Constraint::from_lengths([1, 1])).split(area);
let mut state = TextEditState {
text: self.text.clone(),
text: self.text.to_string(),
cursor: *self.cursor,
};
@@ -1094,32 +1095,6 @@ pub mod widget {
let overline = String::from("▔").repeat(area.width as usize);
let cursor_pos = *self.cursor as u16;
if let Some(key) = ui.input_with_key(|_| true) {
match key {
Key::Char(to_insert)
if (key != Key::Alt('\n'))
&& (key != Key::Char('\n'))
&& (key != Key::Ctrl('\n')) =>
{
state.enter_char(to_insert);
}
Key::Backspace => {
state.delete_char_left();
}
Key::Delete => {
state.delete_char_right();
}
Key::Left => {
state.move_cursor_left();
}
Key::Right => {
state.move_cursor_right();
}
_ => {}
}
response.changed = true;
}
let (label, input, overline) = if !focus && self.dim {
(
Span::from(label_content.clone()).magenta().dim().reversed(),
@@ -1163,6 +1138,32 @@ pub mod widget {
}
}
if let Some(key) = ui.input_with_key(|_| true) {
match key {
Key::Char(to_insert)
if (key != Key::Alt('\n'))
&& (key != Key::Char('\n'))
&& (key != Key::Ctrl('\n')) =>
{
state.enter_char(to_insert);
}
Key::Backspace => {
state.delete_char_left();
}
Key::Delete => {
state.delete_char_right();
}
Key::Left => {
state.move_cursor_left();
}
Key::Right => {
state.move_cursor_right();
}
_ => {}
}
response.changed = true;
}
*self.text = state.text.clone();
*self.cursor = state.cursor;
@@ -1319,3 +1320,105 @@ pub mod widget {
}
}
}
/// A `BufferedValue` that writes updates to an internal
/// buffer. This buffer can be applied or reset.
///
/// Reading from a `BufferedValue` will return the buffer if it's
/// not empty. It will return the actual value otherwise.
#[derive(Clone, Debug)]
pub struct BufferedValue<T>
where
T: Clone,
{
value: T,
buffer: Option<T>,
}
impl<T> BufferedValue<T>
where
T: Clone,
{
pub fn new(value: T) -> Self {
Self {
value,
buffer: None,
}
}
pub fn apply(&mut self) {
if let Some(buffer) = self.buffer.clone() {
self.value = buffer;
}
self.buffer = None;
}
pub fn reset(&mut self) {
self.buffer = None;
}
pub fn write(&mut self, value: T) {
self.buffer = Some(value);
}
pub fn read(&self) -> T {
if let Some(buffer) = self.buffer.clone() {
buffer
} else {
self.value.clone()
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn state_value_read_should_succeed() {
let value = BufferedValue::new(0);
assert_eq!(value.read(), 0);
}
#[test]
fn state_value_read_buffer_should_succeed() {
let mut value = BufferedValue::new(0);
value.write(1);
assert_eq!(value.read(), 1);
}
#[test]
fn state_value_apply_should_succeed() {
let mut value = BufferedValue::new(0);
value.write(1);
assert_eq!(value.read(), 1);
value.apply();
assert_eq!(value.read(), 1);
}
#[test]
fn state_value_reset_should_succeed() {
let mut value = BufferedValue::new(0);
value.write(1);
assert_eq!(value.read(), 1);
value.reset();
assert_eq!(value.read(), 0);
}
#[test]
fn state_value_reset_after_apply_should_succeed() {
let mut value = BufferedValue::new(0);
value.write(1);
assert_eq!(value.read(), 1);
value.apply();
value.reset();
assert_eq!(value.read(), 1);
}
}