ribir_text 0.3.0

A non-intrusive declarative GUI framework, to build modern native/wasm cross-platform applications.
Documentation
use unicode_segmentation::GraphemeCursor as _GraphemeCursor;

use crate::CharacterCursor;

#[derive(Default, Copy, Clone, PartialEq, Eq)]
pub struct GraphemeCursor(pub usize);

impl CharacterCursor for GraphemeCursor {
  fn measure_bytes(&self, text: &str, byte_from: usize, mut char_len: usize) -> usize {
    let mut legacy = _GraphemeCursor::new(byte_from, text.len(), true);
    while char_len > 0 {
      char_len -= 1;
      if legacy.next_boundary(text, 0).unwrap().is_none() {
        break;
      }
    }
    legacy.cur_cursor() - byte_from
  }

  fn move_by_char(&mut self, text: &str, mut offset: isize) {
    let next = offset > 0;
    offset = offset.abs();
    while offset > 0 {
      if !match next {
        true => self.next(text),
        false => self.prev(text),
      } {
        return;
      }
      offset -= 1;
    }
  }

  fn next(&mut self, text: &str) -> bool {
    if self.0 == text.len() {
      return false;
    }

    self.0 += self.measure_bytes(text, self.0, 1);
    true
  }

  fn prev(&mut self, text: &str) -> bool {
    let mut legacy = _GraphemeCursor::new(self.0, text.len(), true);
    if let Some(len) = legacy.prev_boundary(text, 0).unwrap() {
      self.0 = len;
      true
    } else {
      false
    }
  }

  fn set_to(&mut self, text: &str, char_pos: usize) {
    self.0 = self.measure_bytes(text, 0, char_pos);
  }

  fn byte_offset(&self) -> usize { self.0 }

  fn reset(&mut self, byte_offset: usize) { self.0 = byte_offset; }
}

#[cfg(test)]
mod tests {

  use super::*;
  #[test]
  fn test_compose_emoj() {
    let text = "👨‍👩‍👦‍👦";
    let mut cursor = GraphemeCursor(0);
    assert!(25 == cursor.measure_bytes(text, 0, 1));
    cursor.next(text);
    assert!(25 == cursor.byte_offset());
    cursor.prev(text);
    assert!(0 == cursor.byte_offset());
  }
  #[test]
  fn test_char_with_combine() {
    let text = "ee\u{0301}e\u{0301}\u{0301}";
    let mut cursor = GraphemeCursor(0);
    cursor.next(text);
    assert!(1 == cursor.byte_offset());
    cursor.next(text);
    assert!(4 == cursor.byte_offset());
    cursor.next(text);
    assert!(9 == cursor.byte_offset());

    cursor.prev(text);
    assert!(4 == cursor.byte_offset());
    cursor.prev(text);
    assert!(1 == cursor.byte_offset());
    cursor.prev(text);
    assert!(0 == cursor.byte_offset());
  }
}