use epaint::{Galley, text::cursor::CCursor};
use crate::{Event, Id, Key, Modifiers, os::OperatingSystem};
use super::text_cursor_state::{ccursor_next_word, ccursor_previous_word, slice_char_range};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct CCursorRange {
pub primary: CCursor,
pub secondary: CCursor,
pub h_pos: Option<f32>,
}
impl CCursorRange {
#[inline]
pub fn one(ccursor: CCursor) -> Self {
Self {
primary: ccursor,
secondary: ccursor,
h_pos: None,
}
}
#[inline]
pub fn two(min: impl Into<CCursor>, max: impl Into<CCursor>) -> Self {
Self {
primary: max.into(),
secondary: min.into(),
h_pos: None,
}
}
pub fn select_all(galley: &Galley) -> Self {
Self::two(galley.begin(), galley.end())
}
pub fn as_sorted_char_range(&self) -> std::ops::Range<usize> {
let [start, end] = self.sorted_cursors();
std::ops::Range {
start: start.index,
end: end.index,
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.primary == self.secondary
}
pub fn contains(&self, other: Self) -> bool {
let [self_min, self_max] = self.sorted_cursors();
let [other_min, other_max] = other.sorted_cursors();
self_min.index <= other_min.index && other_max.index <= self_max.index
}
pub fn single(&self) -> Option<CCursor> {
if self.is_empty() {
Some(self.primary)
} else {
None
}
}
#[inline]
pub fn is_sorted(&self) -> bool {
let p = self.primary;
let s = self.secondary;
(p.index, p.prefer_next_row) <= (s.index, s.prefer_next_row)
}
#[inline]
pub fn sorted_cursors(&self) -> [CCursor; 2] {
if self.is_sorted() {
[self.primary, self.secondary]
} else {
[self.secondary, self.primary]
}
}
#[inline]
#[deprecated = "Use `self.sorted_cursors` instead."]
pub fn sorted(&self) -> [CCursor; 2] {
self.sorted_cursors()
}
pub fn slice_str<'s>(&self, text: &'s str) -> &'s str {
let [min, max] = self.sorted_cursors();
slice_char_range(text, min.index..max.index)
}
pub fn on_key_press(
&mut self,
os: OperatingSystem,
galley: &Galley,
modifiers: &Modifiers,
key: Key,
) -> bool {
match key {
Key::A if modifiers.command => {
*self = Self::select_all(galley);
true
}
Key::ArrowLeft | Key::ArrowRight if modifiers.is_none() && !self.is_empty() => {
if key == Key::ArrowLeft {
*self = Self::one(self.sorted_cursors()[0]);
} else {
*self = Self::one(self.sorted_cursors()[1]);
}
true
}
Key::ArrowLeft
| Key::ArrowRight
| Key::ArrowUp
| Key::ArrowDown
| Key::Home
| Key::End => {
move_single_cursor(
os,
&mut self.primary,
&mut self.h_pos,
galley,
key,
modifiers,
);
if !modifiers.shift {
self.secondary = self.primary;
}
true
}
Key::P | Key::N | Key::B | Key::F | Key::A | Key::E
if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift =>
{
move_single_cursor(
os,
&mut self.primary,
&mut self.h_pos,
galley,
key,
modifiers,
);
self.secondary = self.primary;
true
}
_ => false,
}
}
pub fn on_event(
&mut self,
os: OperatingSystem,
event: &Event,
galley: &Galley,
_widget_id: Id,
) -> bool {
match event {
Event::Key {
modifiers,
key,
pressed: true,
..
} => self.on_key_press(os, galley, modifiers, *key),
#[cfg(feature = "accesskit")]
Event::AccessKitActionRequest(accesskit::ActionRequest {
action: accesskit::Action::SetTextSelection,
target,
data: Some(accesskit::ActionData::SetTextSelection(selection)),
}) => {
if _widget_id.accesskit_id() == *target {
let primary =
ccursor_from_accesskit_text_position(_widget_id, galley, &selection.focus);
let secondary =
ccursor_from_accesskit_text_position(_widget_id, galley, &selection.anchor);
if let (Some(primary), Some(secondary)) = (primary, secondary) {
*self = Self {
primary,
secondary,
h_pos: None,
};
return true;
}
}
false
}
_ => false,
}
}
}
#[cfg(feature = "accesskit")]
fn ccursor_from_accesskit_text_position(
id: Id,
galley: &Galley,
position: &accesskit::TextPosition,
) -> Option<CCursor> {
let mut total_length = 0usize;
for (i, row) in galley.rows.iter().enumerate() {
let row_id = id.with(i);
if row_id.accesskit_id() == position.node {
return Some(CCursor {
index: total_length + position.character_index,
prefer_next_row: !(position.character_index == row.glyphs.len()
&& !row.ends_with_newline
&& (i + 1) < galley.rows.len()),
});
}
total_length += row.glyphs.len() + (row.ends_with_newline as usize);
}
None
}
fn move_single_cursor(
os: OperatingSystem,
cursor: &mut CCursor,
h_pos: &mut Option<f32>,
galley: &Galley,
key: Key,
modifiers: &Modifiers,
) {
let (new_cursor, new_h_pos) =
if os == OperatingSystem::Mac && modifiers.ctrl && !modifiers.shift {
match key {
Key::A => (galley.cursor_begin_of_row(cursor), None),
Key::E => (galley.cursor_end_of_row(cursor), None),
Key::P => galley.cursor_up_one_row(cursor, *h_pos),
Key::N => galley.cursor_down_one_row(cursor, *h_pos),
Key::B => (galley.cursor_left_one_character(cursor), None),
Key::F => (galley.cursor_right_one_character(cursor), None),
_ => return,
}
} else {
match key {
Key::ArrowLeft => {
if modifiers.alt || modifiers.ctrl {
(ccursor_previous_word(galley, *cursor), None)
} else if modifiers.mac_cmd {
(galley.cursor_begin_of_row(cursor), None)
} else {
(galley.cursor_left_one_character(cursor), None)
}
}
Key::ArrowRight => {
if modifiers.alt || modifiers.ctrl {
(ccursor_next_word(galley, *cursor), None)
} else if modifiers.mac_cmd {
(galley.cursor_end_of_row(cursor), None)
} else {
(galley.cursor_right_one_character(cursor), None)
}
}
Key::ArrowUp => {
if modifiers.command {
(galley.begin(), None)
} else {
galley.cursor_up_one_row(cursor, *h_pos)
}
}
Key::ArrowDown => {
if modifiers.command {
(galley.end(), None)
} else {
galley.cursor_down_one_row(cursor, *h_pos)
}
}
Key::Home => {
if modifiers.ctrl {
(galley.begin(), None)
} else {
(galley.cursor_begin_of_row(cursor), None)
}
}
Key::End => {
if modifiers.ctrl {
(galley.end(), None)
} else {
(galley.cursor_end_of_row(cursor), None)
}
}
_ => unreachable!(),
}
};
*cursor = new_cursor;
*h_pos = new_h_pos;
}