mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-11-15 16:00:38 +08:00
lib: Use a buffered value for text edits in imUI
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user