feat: Add rtl support to text input
This commit is contained in:
parent
de2982b37e
commit
9b2857083e
6 changed files with 411 additions and 134 deletions
|
|
@ -1,10 +1,12 @@
|
|||
//! Track the cursor of a text input.
|
||||
use crate::core::text::Affinity;
|
||||
use crate::text_input::Value;
|
||||
|
||||
/// The cursor of a text input.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Cursor {
|
||||
state: State,
|
||||
affinity: Affinity,
|
||||
}
|
||||
|
||||
/// The state of a [`Cursor`].
|
||||
|
|
@ -26,6 +28,7 @@ impl Default for Cursor {
|
|||
fn default() -> Self {
|
||||
Cursor {
|
||||
state: State::Index(0),
|
||||
affinity: Affinity::Before,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -186,4 +189,52 @@ impl Cursor {
|
|||
State::Selection { start, end } => start.max(end),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current cursor [`Affinity`].
|
||||
pub fn affinity(&self) -> Affinity {
|
||||
self.affinity
|
||||
}
|
||||
|
||||
/// Sets the cursor [`Affinity`].
|
||||
pub fn set_affinity(&mut self, affinity: Affinity) {
|
||||
self.affinity = affinity;
|
||||
}
|
||||
|
||||
/// Moves the cursor in a visual direction, accounting for RTL text.
|
||||
///
|
||||
/// `forward` = `true` is visually rightward
|
||||
/// RTL text flips the logical direction so that pressing the right-arrow key
|
||||
/// still moves the caret forward visually.
|
||||
pub fn move_visual(
|
||||
&mut self,
|
||||
forward: bool,
|
||||
by_words: bool,
|
||||
rtl: bool,
|
||||
value: &Value,
|
||||
) {
|
||||
match (forward ^ rtl, by_words) {
|
||||
(true, false) => self.move_right(value),
|
||||
(true, true) => self.move_right_by_words(value),
|
||||
(false, false) => self.move_left(value),
|
||||
(false, true) => self.move_left_by_words(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the selection in a visual direction, accounting for RTL text.
|
||||
///
|
||||
/// See [`Cursor::move_visual`] for the `forward` / `rtl` semantics.
|
||||
pub fn select_visual(
|
||||
&mut self,
|
||||
forward: bool,
|
||||
by_words: bool,
|
||||
rtl: bool,
|
||||
value: &Value,
|
||||
) {
|
||||
match (forward ^ rtl, by_words) {
|
||||
(true, false) => self.select_right(value),
|
||||
(true, true) => self.select_right_by_words(value),
|
||||
(false, false) => self.select_left(value),
|
||||
(false, true) => self.select_left_by_words(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,26 @@ impl Value {
|
|||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a grapheme index to a byte index in the underlying string.
|
||||
pub fn byte_index_at_grapheme(&self, grapheme_index: usize) -> usize {
|
||||
self.graphemes[..grapheme_index.min(self.graphemes.len())]
|
||||
.iter()
|
||||
.map(|g| g.len())
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Converts a byte index to a grapheme index.
|
||||
pub fn grapheme_index_at_byte(&self, byte_index: usize) -> usize {
|
||||
let mut bytes = 0;
|
||||
for (i, g) in self.graphemes.iter().enumerate() {
|
||||
if bytes >= byte_index {
|
||||
return i;
|
||||
}
|
||||
bytes += g.len();
|
||||
}
|
||||
self.graphemes.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Value {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue