use crate::theme::Text;
use kas_macros::autoimpl;
use kas_text::format::FormattableText;
use std::ops::Range;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CursorRange {
sel: usize,
edit: usize,
}
impl From<usize> for CursorRange {
#[inline]
fn from(index: usize) -> Self {
CursorRange {
sel: index,
edit: index,
}
}
}
impl From<Range<usize>> for CursorRange {
#[inline]
fn from(range: Range<usize>) -> Self {
CursorRange {
sel: range.start,
edit: range.end,
}
}
}
impl CursorRange {
#[inline]
pub fn new(sel: usize, edit: usize) -> Self {
CursorRange { sel, edit }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.edit == self.sel
}
#[inline]
pub fn set_empty(&mut self) {
self.sel = self.edit;
}
pub fn sel_index(&self) -> usize {
self.sel
}
pub fn edit_index(&self) -> usize {
self.edit
}
pub fn range(&self) -> Range<usize> {
let mut range = self.edit..self.sel;
if range.start > range.end {
std::mem::swap(&mut range.start, &mut range.end);
}
range
}
}
#[derive(Clone, Debug, Default)]
#[autoimpl(Deref, DerefMut using self.cursor)]
pub struct SelectionHelper {
cursor: CursorRange,
anchor: usize,
}
impl<T: Into<CursorRange>> From<T> for SelectionHelper {
fn from(x: T) -> Self {
let cursor = x.into();
SelectionHelper {
cursor,
anchor: cursor.sel,
}
}
}
impl SelectionHelper {
#[inline]
pub fn set_cursor(&mut self, index: usize) {
self.cursor.sel = index;
self.cursor.edit = index;
self.anchor = index;
}
#[inline]
pub fn set_edit_index(&mut self, index: usize) {
self.edit = index;
}
#[inline]
pub fn set_sel_index(&mut self, index: usize) {
self.sel = index;
self.anchor = index;
}
#[inline]
pub fn set_sel_index_only(&mut self, index: usize) {
self.sel = index;
}
#[inline]
pub fn set_max_len(&mut self, len: usize) {
self.edit = self.edit.min(len);
self.sel = self.sel.min(len);
self.anchor = self.anchor.min(len);
}
#[inline]
pub fn set_anchor(&mut self, clear: bool) {
self.anchor = self.edit;
if clear {
self.sel = self.edit;
}
}
pub fn expand<T: FormattableText>(&mut self, text: &Text<T>, lines: bool) {
let string = text.as_str();
let mut range = self.edit..self.anchor;
if range.start > range.end {
std::mem::swap(&mut range.start, &mut range.end);
}
let (mut start, mut end);
if !lines {
end = string[range.start..]
.char_indices()
.nth(1)
.map(|(i, _)| range.start + i)
.unwrap_or(string.len());
start = string[0..end]
.split_word_bound_indices()
.next_back()
.map(|(index, _)| index)
.unwrap_or(0);
end = string[start..]
.split_word_bound_indices()
.find_map(|(index, _)| {
let pos = start + index;
(pos >= range.end).then_some(pos)
})
.unwrap_or(string.len());
} else {
start = match text.find_line(range.start) {
Ok(Some(r)) => r.1.start,
_ => 0,
};
end = match text.find_line(range.end) {
Ok(Some(r)) => r.1.end,
_ => string.len(),
};
}
if self.edit < self.sel {
std::mem::swap(&mut start, &mut end);
}
self.sel = start;
self.edit = end;
}
pub fn delete_range(&mut self, range: Range<usize>) {
let len = range.len();
let adjust = |index: usize| -> usize {
if index >= range.end {
index - len
} else if index > range.start {
range.start
} else {
index
}
};
self.edit = adjust(self.edit);
self.sel = adjust(self.sel);
self.anchor = adjust(self.anchor);
}
}