use std::{borrow::Cow, ops::Range};
use crate::kurbo::{Point, Rect};
use crate::prelude::*;
pub trait InputHandler {
fn is_empty(&self) -> bool {
self.len() == 0
}
fn len(&self) -> usize;
fn slice<'a>(&'a self, range: Range<usize>) -> Cow<'a, str>;
fn selection(&self) -> Range<usize>;
fn set_selection(&mut self, selection: Range<usize>);
fn composition_range(&self) -> Option<Range<usize>>;
fn set_composition_range(&mut self, range: Option<Range<usize>>);
fn replace_range(&mut self, range: Range<usize>, text: &str);
fn handle_action(&mut self, action: Action) -> bool;
fn hit_test_point(&self, point: Point) -> Option<Cursor>;
fn bounding_box_for_range(&self, range: Range<usize>) -> Option<Rect>;
fn utf8_range_utf16_len(&self, range: Range<usize>) -> Option<usize> {
if range.start > range.end || range.end > self.len() {
return None;
}
let slice = self.slice(0..self.len());
if !slice.is_char_boundary(range.start) || !slice.is_char_boundary(range.end) {
return None;
}
Some(slice[range].chars().map(|c| c.len_utf16()).sum())
}
fn utf16_range_to_utf8_range(&self, range: Range<usize>) -> Option<Range<usize>> {
if range.start > range.end {
return None;
}
if range.start == 0 && range.end == 0 {
return Some(0..0);
}
let text = self.slice(0..self.len());
let eof_byte = text.len();
let mut current_utf16 = 0usize;
let mut byte_start: Option<usize> = None;
let mut byte_end: Option<usize> = None;
for (byte_idx, ch) in text.char_indices() {
let next_utf16 = current_utf16 + ch.len_utf16();
let next_byte = byte_idx + ch.len_utf8();
if byte_start.is_none() {
if range.start == current_utf16 {
byte_start = Some(byte_idx);
} else if range.start == next_utf16 {
byte_start = Some(next_byte);
}
}
if byte_end.is_none() {
if range.end == current_utf16 {
byte_end = Some(byte_idx);
} else if range.end == next_utf16 {
byte_end = Some(next_byte);
}
}
current_utf16 = next_utf16;
if byte_start.is_some() && byte_end.is_some() {
break;
}
}
if byte_start.is_none() && range.start == current_utf16 {
byte_start = Some(eof_byte);
}
if byte_end.is_none() && range.end == current_utf16 {
byte_end = Some(eof_byte);
}
if range.end > current_utf16 {
return None;
}
match (byte_start, byte_end) {
(Some(s), Some(e)) if s <= e => Some(s..e),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Action {
Move(Movement),
MoveSelecting(Movement),
Delete(Movement),
Select(SelectionUnit),
InsertNewLine,
InsertTab,
InsertBacktab,
Copy,
Cut,
Paste,
Cancel,
}
impl Action {
pub fn edits_text(&self) -> bool {
matches!(self, Action::Delete(_) | Action::InsertNewLine | Action::InsertTab | Action::InsertBacktab | Action::Cut | Action::Paste)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Movement {
Grapheme(HorizontalDirection),
Word(HorizontalDirection),
Line(HorizontalDirection),
Paragraph(HorizontalDirection),
Document(HorizontalDirection),
Vertical(VerticalDirection),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HorizontalDirection {
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerticalDirection {
Up,
Down,
PageUp,
PageDown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SelectionUnit {
Word,
Line,
Paragraph,
All,
}