use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use crate::dom::{DomId, DomNodeId, NodeId};
use crate::geom::{LogicalPosition, LogicalRect};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ContentIndex {
pub run_index: u32,
pub item_index: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C)]
pub struct GraphemeClusterId {
pub source_run: u32,
pub start_byte_in_run: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub enum CursorAffinity {
Leading,
Trailing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub struct TextCursor {
pub cluster_id: GraphemeClusterId,
pub affinity: CursorAffinity,
}
impl_option!(
TextCursor,
OptionTextCursor,
[Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd]
);
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct SelectionRange {
pub start: TextCursor,
pub end: TextCursor,
}
impl_option!(
SelectionRange,
OptionSelectionRange,
[Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd]
);
impl_vec!(SelectionRange, SelectionRangeVec, SelectionRangeVecDestructor, SelectionRangeVecDestructorType, SelectionRangeVecSlice, OptionSelectionRange);
impl_vec_debug!(SelectionRange, SelectionRangeVec);
impl_vec_clone!(
SelectionRange,
SelectionRangeVec,
SelectionRangeVecDestructor
);
impl_vec_partialeq!(SelectionRange, SelectionRangeVec);
impl_vec_partialord!(SelectionRange, SelectionRangeVec);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum Selection {
Cursor(TextCursor),
Range(SelectionRange),
}
impl_option!(
Selection,
OptionSelection,
[Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord]
);
impl_vec!(Selection, SelectionVec, SelectionVecDestructor, SelectionVecDestructorType, SelectionVecSlice, OptionSelection);
impl_vec_debug!(Selection, SelectionVec);
impl_vec_clone!(Selection, SelectionVec, SelectionVecDestructor);
impl_vec_partialeq!(Selection, SelectionVec);
impl_vec_partialord!(Selection, SelectionVec);
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct SelectionState {
pub selections: SelectionVec,
pub node_id: DomNodeId,
}
impl SelectionState {
pub fn add(&mut self, new_selection: Selection) {
let mut selections: Vec<Selection> = self.selections.as_ref().to_vec();
selections.push(new_selection);
selections.sort_unstable();
selections.dedup(); self.selections = selections.into();
}
pub fn set_cursor(&mut self, cursor: TextCursor) {
self.selections = vec![Selection::Cursor(cursor)].into();
}
}
impl_option!(
SelectionState,
OptionSelectionState,
copy = false,
clone = false,
[Debug, Clone, PartialEq]
);
#[derive(Debug, Clone, PartialEq)]
pub struct SelectionAnchor {
pub ifc_root_node_id: NodeId,
pub cursor: TextCursor,
pub char_bounds: LogicalRect,
pub mouse_position: LogicalPosition,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SelectionFocus {
pub ifc_root_node_id: NodeId,
pub cursor: TextCursor,
pub mouse_position: LogicalPosition,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NodeSelectionType {
Anchor,
Focus,
InBetween,
AnchorAndFocus,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TextSelection {
pub dom_id: DomId,
pub anchor: SelectionAnchor,
pub focus: SelectionFocus,
pub affected_nodes: BTreeMap<NodeId, SelectionRange>,
pub is_forward: bool,
}
impl TextSelection {
pub fn new_collapsed(
dom_id: DomId,
ifc_root_node_id: NodeId,
cursor: TextCursor,
char_bounds: LogicalRect,
mouse_position: LogicalPosition,
) -> Self {
let anchor = SelectionAnchor {
ifc_root_node_id,
cursor,
char_bounds,
mouse_position,
};
let focus = SelectionFocus {
ifc_root_node_id,
cursor,
mouse_position,
};
let mut affected_nodes = BTreeMap::new();
affected_nodes.insert(ifc_root_node_id, SelectionRange {
start: cursor,
end: cursor,
});
TextSelection {
dom_id,
anchor,
focus,
affected_nodes,
is_forward: true, }
}
pub fn is_collapsed(&self) -> bool {
self.anchor.ifc_root_node_id == self.focus.ifc_root_node_id
&& self.anchor.cursor == self.focus.cursor
}
pub fn get_range_for_node(&self, ifc_root_node_id: &NodeId) -> Option<&SelectionRange> {
self.affected_nodes.get(ifc_root_node_id)
}
pub fn contains_node(&self, ifc_root_node_id: &NodeId) -> bool {
self.affected_nodes.contains_key(ifc_root_node_id)
}
pub fn get_node_selection_type(&self, ifc_root_node_id: &NodeId) -> Option<NodeSelectionType> {
if !self.affected_nodes.contains_key(ifc_root_node_id) {
return None;
}
let is_anchor = *ifc_root_node_id == self.anchor.ifc_root_node_id;
let is_focus = *ifc_root_node_id == self.focus.ifc_root_node_id;
Some(match (is_anchor, is_focus) {
(true, true) => NodeSelectionType::AnchorAndFocus,
(true, false) => NodeSelectionType::Anchor,
(false, true) => NodeSelectionType::Focus,
(false, false) => NodeSelectionType::InBetween,
})
}
}
impl_option!(
TextSelection,
OptionTextSelection,
copy = false,
clone = false,
[Debug, Clone, PartialEq]
);