Fix: dragging to above/below a `TextEdit` or `Label` will select text to begin/end (#3858)
This also adds a `Galley::begin` method to match `Galley::end` (both returning cursor positions). * Part of https://github.com/emilk/egui/issues/3816
This commit is contained in:
parent
d319489479
commit
ff39fd6195
|
|
@ -38,7 +38,7 @@ impl CursorRange {
|
|||
|
||||
/// Select all the text in a galley
|
||||
pub fn select_all(galley: &Galley) -> Self {
|
||||
Self::two(Cursor::default(), galley.end())
|
||||
Self::two(galley.begin(), galley.end())
|
||||
}
|
||||
|
||||
pub fn as_ccursor_range(&self) -> CCursorRange {
|
||||
|
|
@ -342,7 +342,7 @@ fn move_single_cursor(
|
|||
Key::ArrowUp => {
|
||||
if modifiers.command {
|
||||
// mac and windows behavior
|
||||
*cursor = Cursor::default();
|
||||
*cursor = galley.begin();
|
||||
} else {
|
||||
*cursor = galley.cursor_up_one_row(cursor);
|
||||
}
|
||||
|
|
@ -359,7 +359,7 @@ fn move_single_cursor(
|
|||
Key::Home => {
|
||||
if modifiers.ctrl {
|
||||
// windows behavior
|
||||
*cursor = Cursor::default();
|
||||
*cursor = galley.begin();
|
||||
} else {
|
||||
*cursor = galley.cursor_begin_of_row(cursor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,27 @@ pub struct TextCursorState {
|
|||
ccursor_range: Option<CCursorRange>,
|
||||
}
|
||||
|
||||
impl From<CursorRange> for TextCursorState {
|
||||
fn from(cursor_range: CursorRange) -> Self {
|
||||
Self {
|
||||
cursor_range: Some(cursor_range),
|
||||
ccursor_range: Some(CCursorRange {
|
||||
primary: cursor_range.primary.ccursor,
|
||||
secondary: cursor_range.secondary.ccursor,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CCursorRange> for TextCursorState {
|
||||
fn from(ccursor_range: CCursorRange) -> Self {
|
||||
Self {
|
||||
cursor_range: None,
|
||||
ccursor_range: Some(ccursor_range),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextCursorState {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.cursor_range.is_none() && self.ccursor_range.is_none()
|
||||
|
|
@ -33,7 +54,7 @@ impl TextCursorState {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn range(&mut self, galley: &Galley) -> Option<CursorRange> {
|
||||
pub fn range(&self, galley: &Galley) -> Option<CursorRange> {
|
||||
self.cursor_range
|
||||
.map(|cursor_range| {
|
||||
// We only use the PCursor (paragraph number, and character offset within that paragraph).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
//! Different types of text cursors, i.e. ways to point into a [`super::Galley`].
|
||||
|
||||
/// Character cursor
|
||||
/// Character cursor.
|
||||
///
|
||||
/// The default cursor is zero.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct CCursor {
|
||||
|
|
@ -110,9 +112,12 @@ impl PartialEq for PCursor {
|
|||
}
|
||||
|
||||
/// All different types of cursors together.
|
||||
///
|
||||
/// They all point to the same place, but in their own different ways.
|
||||
/// pcursor/rcursor can also point to after the end of the paragraph/row.
|
||||
/// Does not implement `PartialEq` because you must think which cursor should be equivalent.
|
||||
///
|
||||
/// The default cursor is the zero-cursor, to the first character.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Cursor {
|
||||
|
|
|
|||
|
|
@ -712,8 +712,25 @@ impl Galley {
|
|||
self.pos_from_pcursor(cursor.pcursor) // pcursor is what TextEdit stores
|
||||
}
|
||||
|
||||
/// Cursor at the given position within the galley
|
||||
/// Cursor at the given position within the galley.
|
||||
///
|
||||
/// A cursor above the galley is considered
|
||||
/// same as a cursor at the start,
|
||||
/// and a cursor below the galley is considered
|
||||
/// same as a cursor at the end.
|
||||
/// This allows implementing text-selection by dragging above/below the galley.
|
||||
pub fn cursor_from_pos(&self, pos: Vec2) -> Cursor {
|
||||
if let Some(first_row) = self.rows.first() {
|
||||
if pos.y < first_row.min_y() {
|
||||
return self.begin();
|
||||
}
|
||||
}
|
||||
if let Some(last_row) = self.rows.last() {
|
||||
if last_row.max_y() < pos.y {
|
||||
return self.end();
|
||||
}
|
||||
}
|
||||
|
||||
let mut best_y_dist = f32::INFINITY;
|
||||
let mut cursor = Cursor::default();
|
||||
|
||||
|
|
@ -721,7 +738,7 @@ impl Galley {
|
|||
let mut pcursor_it = PCursor::default();
|
||||
|
||||
for (row_nr, row) in self.rows.iter().enumerate() {
|
||||
let is_pos_within_row = pos.y >= row.min_y() && pos.y <= row.max_y();
|
||||
let is_pos_within_row = row.min_y() <= pos.y && pos.y <= row.max_y();
|
||||
let y_dist = (row.min_y() - pos.y).abs().min((row.max_y() - pos.y).abs());
|
||||
if is_pos_within_row || y_dist < best_y_dist {
|
||||
best_y_dist = y_dist;
|
||||
|
|
@ -755,12 +772,22 @@ impl Galley {
|
|||
pcursor_it.offset += row.char_count_including_newline();
|
||||
}
|
||||
}
|
||||
|
||||
cursor
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Cursor positions
|
||||
impl Galley {
|
||||
/// Cursor to the first character.
|
||||
///
|
||||
/// This is the same as [`Cursor::default`].
|
||||
#[inline]
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn begin(&self) -> Cursor {
|
||||
Cursor::default()
|
||||
}
|
||||
|
||||
/// Cursor to one-past last character.
|
||||
pub fn end(&self) -> Cursor {
|
||||
if self.rows.is_empty() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue