mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 14:20:04 -04:00
Properly handle row repositioning
This commit is contained in:
@@ -39,11 +39,11 @@ pub fn update_accesskit_for_text_widget(
|
|||||||
};
|
};
|
||||||
|
|
||||||
ctx.with_accessibility_parent(parent_id, || {
|
ctx.with_accessibility_parent(parent_id, || {
|
||||||
for (row_index, (row, offset)) in galley.rows.iter().enumerate() {
|
for (row_index, row) in galley.rows.iter().enumerate() {
|
||||||
let row_id = parent_id.with(row_index);
|
let row_id = parent_id.with(row_index);
|
||||||
ctx.accesskit_node_builder(row_id, |builder| {
|
ctx.accesskit_node_builder(row_id, |builder| {
|
||||||
builder.set_role(accesskit::Role::TextRun);
|
builder.set_role(accesskit::Role::TextRun);
|
||||||
let rect = row.rect.translate(offset.to_vec2() + galley_pos.to_vec2());
|
let rect = row.rect().translate(galley_pos.to_vec2());
|
||||||
builder.set_bounds(accesskit::Rect {
|
builder.set_bounds(accesskit::Rect {
|
||||||
x0: rect.min.x.into(),
|
x0: rect.min.x.into(),
|
||||||
y0: rect.min.y.into(),
|
y0: rect.min.y.into(),
|
||||||
@@ -74,14 +74,14 @@ pub fn update_accesskit_for_text_widget(
|
|||||||
let old_len = value.len();
|
let old_len = value.len();
|
||||||
value.push(glyph.chr);
|
value.push(glyph.chr);
|
||||||
character_lengths.push((value.len() - old_len) as _);
|
character_lengths.push((value.len() - old_len) as _);
|
||||||
character_positions.push(glyph.pos.x - row.rect.min.x);
|
character_positions.push(glyph.pos.x - row.pos.x);
|
||||||
character_widths.push(glyph.advance_width);
|
character_widths.push(glyph.advance_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if row.ends_with_newline {
|
if row.ends_with_newline {
|
||||||
value.push('\n');
|
value.push('\n');
|
||||||
character_lengths.push(1);
|
character_lengths.push(1);
|
||||||
character_positions.push(row.rect.max.x - row.rect.min.x);
|
character_positions.push(row.size.x);
|
||||||
character_widths.push(0.0);
|
character_widths.push(0.0);
|
||||||
}
|
}
|
||||||
word_lengths.push((character_lengths.len() - last_word_start) as _);
|
word_lengths.push((character_lengths.len() - last_word_start) as _);
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ fn ccursor_from_accesskit_text_position(
|
|||||||
position: &accesskit::TextPosition,
|
position: &accesskit::TextPosition,
|
||||||
) -> Option<CCursor> {
|
) -> Option<CCursor> {
|
||||||
let mut total_length = 0usize;
|
let mut total_length = 0usize;
|
||||||
for (i, (row, _)) in galley.rows.iter().enumerate() {
|
for (i, row) in galley.rows.iter().enumerate() {
|
||||||
let row_id = id.with(i);
|
let row_id = id.with(i);
|
||||||
if row_id.accesskit_id() == position.node {
|
if row_id.accesskit_id() == position.node {
|
||||||
return Some(CCursor {
|
return Some(CCursor {
|
||||||
|
|||||||
@@ -179,10 +179,10 @@ impl LabelSelectionState {
|
|||||||
if let epaint::Shape::Text(text_shape) = &mut shape.shape {
|
if let epaint::Shape::Text(text_shape) = &mut shape.shape {
|
||||||
let galley = Arc::make_mut(&mut text_shape.galley);
|
let galley = Arc::make_mut(&mut text_shape.galley);
|
||||||
for row_selection in row_selections {
|
for row_selection in row_selections {
|
||||||
if let Some((row, _)) =
|
if let Some(placed_row) =
|
||||||
galley.rows.get_mut(row_selection.row)
|
galley.rows.get_mut(row_selection.row)
|
||||||
{
|
{
|
||||||
let row = Arc::make_mut(row);
|
let row = Arc::make_mut(&mut placed_row.row);
|
||||||
for vertex_index in row_selection.vertex_indices {
|
for vertex_index in row_selection.vertex_indices {
|
||||||
if let Some(vertex) = row
|
if let Some(vertex) = row
|
||||||
.visuals
|
.visuals
|
||||||
@@ -662,8 +662,8 @@ fn selected_text(galley: &Galley, cursor_range: &CursorRange) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn estimate_row_height(galley: &Galley) -> f32 {
|
fn estimate_row_height(galley: &Galley) -> f32 {
|
||||||
if let Some((row, _)) = galley.rows.first() {
|
if let Some(placed_row) = galley.rows.first() {
|
||||||
row.rect.height()
|
placed_row.height()
|
||||||
} else {
|
} else {
|
||||||
galley.size().y
|
galley.size().y
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,26 +31,27 @@ pub fn paint_text_selection(
|
|||||||
let max = max.rcursor;
|
let max = max.rcursor;
|
||||||
|
|
||||||
for ri in min.row..=max.row {
|
for ri in min.row..=max.row {
|
||||||
let (row, _) = &mut galley.rows[ri];
|
let placed_row = &mut galley.rows[ri];
|
||||||
let row = Arc::make_mut(row);
|
|
||||||
|
|
||||||
let left = if ri == min.row {
|
let left = if ri == min.row {
|
||||||
row.x_offset(min.column)
|
placed_row.x_offset(min.column)
|
||||||
} else {
|
} else {
|
||||||
row.rect.left()
|
0.0
|
||||||
};
|
};
|
||||||
let right = if ri == max.row {
|
let right = if ri == max.row {
|
||||||
row.x_offset(max.column)
|
placed_row.x_offset(max.column)
|
||||||
} else {
|
} else {
|
||||||
let newline_size = if row.ends_with_newline {
|
let newline_size = if placed_row.ends_with_newline {
|
||||||
row.height() / 2.0 // visualize that we select the newline
|
placed_row.height() / 2.0 // visualize that we select the newline
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
row.rect.right() + newline_size
|
placed_row.size.x + newline_size
|
||||||
};
|
};
|
||||||
|
|
||||||
let rect = Rect::from_min_max(pos2(left, row.min_y()), pos2(right, row.max_y()));
|
let rect = Rect::from_min_max(pos2(left, 0.0), pos2(right, placed_row.size.y));
|
||||||
|
|
||||||
|
let row = Arc::make_mut(&mut placed_row.row);
|
||||||
let mesh = &mut row.visuals.mesh;
|
let mesh = &mut row.visuals.mesh;
|
||||||
|
|
||||||
// Time to insert the selection rectangle into the row mesh.
|
// Time to insert the selection rectangle into the row mesh.
|
||||||
|
|||||||
@@ -640,8 +640,8 @@ impl WidgetText {
|
|||||||
Self::RichText(text) => text.font_height(fonts, style),
|
Self::RichText(text) => text.font_height(fonts, style),
|
||||||
Self::LayoutJob(job) => job.font_height(fonts),
|
Self::LayoutJob(job) => job.font_height(fonts),
|
||||||
Self::Galley(galley) => {
|
Self::Galley(galley) => {
|
||||||
if let Some((row, _)) = galley.rows.first() {
|
if let Some(placed_row) = galley.rows.first() {
|
||||||
row.height()
|
placed_row.height()
|
||||||
} else {
|
} else {
|
||||||
galley.size().y
|
galley.size().y
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,13 +194,10 @@ impl Label {
|
|||||||
let pos = pos2(ui.max_rect().left(), ui.cursor().top());
|
let pos = pos2(ui.max_rect().left(), ui.cursor().top());
|
||||||
assert!(!galley.rows.is_empty(), "Galleys are never empty");
|
assert!(!galley.rows.is_empty(), "Galleys are never empty");
|
||||||
// collect a response from many rows:
|
// collect a response from many rows:
|
||||||
let rect = galley.rows[0]
|
let rect = galley.rows[0].rect().translate(pos.to_vec2());
|
||||||
.0
|
|
||||||
.rect
|
|
||||||
.translate(galley.rows[0].1.to_vec2() + pos.to_vec2());
|
|
||||||
let mut response = ui.allocate_rect(rect, sense);
|
let mut response = ui.allocate_rect(rect, sense);
|
||||||
for (row, offset) in galley.rows.iter().skip(1) {
|
for placed_row in galley.rows.iter().skip(1) {
|
||||||
let rect = row.rect.translate(offset.to_vec2() + pos.to_vec2());
|
let rect = placed_row.rect().translate(pos.to_vec2());
|
||||||
response |= ui.allocate_rect(rect, sense);
|
response |= ui.allocate_rect(rect, sense);
|
||||||
}
|
}
|
||||||
(pos, galley, response)
|
(pos, galley, response)
|
||||||
|
|||||||
@@ -433,9 +433,8 @@ impl Shape {
|
|||||||
|
|
||||||
// Scale text:
|
// Scale text:
|
||||||
let galley = Arc::make_mut(&mut text_shape.galley);
|
let galley = Arc::make_mut(&mut text_shape.galley);
|
||||||
for (row, offset) in &mut galley.rows {
|
for placed_row in &mut galley.rows {
|
||||||
let row = Arc::make_mut(row);
|
let row = Arc::make_mut(&mut placed_row.row);
|
||||||
*offset = *offset * transform.scaling;
|
|
||||||
row.visuals.mesh_bounds = transform.scaling * row.visuals.mesh_bounds;
|
row.visuals.mesh_bounds = transform.scaling * row.visuals.mesh_bounds;
|
||||||
for v in &mut row.visuals.mesh.vertices {
|
for v in &mut row.visuals.mesh.vertices {
|
||||||
v.pos = Pos2::new(transform.scaling * v.pos.x, transform.scaling * v.pos.y);
|
v.pos = Pos2::new(transform.scaling * v.pos.x, transform.scaling * v.pos.y);
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ pub fn adjust_colors(
|
|||||||
|
|
||||||
if !galley.is_empty() {
|
if !galley.is_empty() {
|
||||||
let galley = std::sync::Arc::make_mut(galley);
|
let galley = std::sync::Arc::make_mut(galley);
|
||||||
for (row, _) in &mut galley.rows {
|
for placed_row in &mut galley.rows {
|
||||||
let row = Arc::make_mut(row);
|
let row = Arc::make_mut(&mut placed_row.row);
|
||||||
for vertex in &mut row.visuals.mesh.vertices {
|
for vertex in &mut row.visuals.mesh.vertices {
|
||||||
adjust_color(&mut vertex.color);
|
adjust_color(&mut vertex.color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,14 +88,10 @@ impl AllocInfo {
|
|||||||
pub fn from_galley(galley: &Galley) -> Self {
|
pub fn from_galley(galley: &Galley) -> Self {
|
||||||
Self::from_slice(galley.text().as_bytes())
|
Self::from_slice(galley.text().as_bytes())
|
||||||
+ Self::from_slice(&galley.rows)
|
+ Self::from_slice(&galley.rows)
|
||||||
+ galley
|
+ galley.rows.iter().map(Self::from_galley_row).sum()
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.map(|(row, _)| Self::from_galley_row(row))
|
|
||||||
.sum()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_galley_row(row: &crate::text::Row) -> Self {
|
fn from_galley_row(row: &crate::text::PlacedRow) -> Self {
|
||||||
Self::from_mesh(&row.visuals.mesh) + Self::from_slice(&row.glyphs)
|
Self::from_mesh(&row.visuals.mesh) + Self::from_slice(&row.glyphs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,8 +213,8 @@ impl PaintStats {
|
|||||||
self.shape_text += AllocInfo::from_galley(&text_shape.galley);
|
self.shape_text += AllocInfo::from_galley(&text_shape.galley);
|
||||||
|
|
||||||
for row in &text_shape.galley.rows {
|
for row in &text_shape.galley.rows {
|
||||||
self.text_shape_indices += AllocInfo::from_slice(&row.0.visuals.mesh.indices);
|
self.text_shape_indices += AllocInfo::from_slice(&row.visuals.mesh.indices);
|
||||||
self.text_shape_vertices += AllocInfo::from_slice(&row.0.visuals.mesh.vertices);
|
self.text_shape_vertices += AllocInfo::from_slice(&row.visuals.mesh.vertices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Shape::Mesh(mesh) => {
|
Shape::Mesh(mesh) => {
|
||||||
|
|||||||
@@ -1778,12 +1778,12 @@ impl Tessellator {
|
|||||||
|
|
||||||
let rotator = Rot2::from_angle(*angle);
|
let rotator = Rot2::from_angle(*angle);
|
||||||
|
|
||||||
for (row, row_pos) in &galley.rows {
|
for row in &galley.rows {
|
||||||
if row.visuals.mesh.is_empty() {
|
if row.visuals.mesh.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let final_pos = galley_pos + row_pos.to_vec2();
|
let final_pos = galley_pos + row.pos.to_vec2();
|
||||||
|
|
||||||
let mut row_rect = row.visuals.mesh_bounds;
|
let mut row_rect = row.visuals.mesh_bounds;
|
||||||
if *angle != 0.0 {
|
if *angle != 0.0 {
|
||||||
|
|||||||
@@ -805,12 +805,21 @@ impl GalleyCache {
|
|||||||
let current_offset = emath::vec2(0.0, merged_galley.rect.height());
|
let current_offset = emath::vec2(0.0, merged_galley.rect.height());
|
||||||
merged_galley
|
merged_galley
|
||||||
.rows
|
.rows
|
||||||
.extend(galley.rows.iter().map(|(row, prev_offset)| {
|
.extend(galley.rows.iter().map(|placed_row| {
|
||||||
merged_galley.mesh_bounds =
|
let new_pos = placed_row.pos + current_offset;
|
||||||
merged_galley.mesh_bounds.union(row.visuals.mesh_bounds);
|
merged_galley.mesh_bounds = merged_galley
|
||||||
|
.mesh_bounds
|
||||||
|
.union(placed_row.visuals.mesh_bounds.translate(new_pos.to_vec2()));
|
||||||
|
|
||||||
(row.clone(), *prev_offset + current_offset)
|
super::PlacedRow {
|
||||||
|
row: placed_row.row.clone(),
|
||||||
|
pos: new_pos,
|
||||||
|
ends_with_newline: placed_row.ends_with_newline,
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
if let Some(last) = merged_galley.rows.last_mut() {
|
||||||
|
last.ends_with_newline = true;
|
||||||
|
}
|
||||||
merged_galley.rect = merged_galley
|
merged_galley.rect = merged_galley
|
||||||
.rect
|
.rect
|
||||||
.union(galley.rect.translate(current_offset));
|
.union(galley.rect.translate(current_offset));
|
||||||
@@ -822,6 +831,10 @@ impl GalleyCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(last) = merged_galley.rows.last_mut() {
|
||||||
|
last.ends_with_newline = false;
|
||||||
|
}
|
||||||
|
|
||||||
merged_galley
|
merged_galley
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use emath::{pos2, vec2, Align, NumExt, Pos2, Rect, Vec2};
|
|||||||
|
|
||||||
use crate::{stroke::PathStroke, text::font::Font, Color32, Mesh, Stroke, Vertex};
|
use crate::{stroke::PathStroke, text::font::Font, Color32, Mesh, Stroke, Vertex};
|
||||||
|
|
||||||
use super::{FontsImpl, Galley, Glyph, LayoutJob, LayoutSection, Row, RowVisuals};
|
use super::{FontsImpl, Galley, Glyph, LayoutJob, LayoutSection, PlacedRow, Row, RowVisuals};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -96,11 +96,11 @@ pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
|
|||||||
let mut elided = false;
|
let mut elided = false;
|
||||||
let mut rows = rows_from_paragraphs(paragraphs, &job, &mut elided);
|
let mut rows = rows_from_paragraphs(paragraphs, &job, &mut elided);
|
||||||
if elided {
|
if elided {
|
||||||
if let Some((last_row, _)) = rows.last_mut() {
|
if let Some(last_placed) = rows.last_mut() {
|
||||||
let last_row = Arc::get_mut(last_row).unwrap();
|
let last_row = Arc::get_mut(&mut last_placed.row).unwrap();
|
||||||
replace_last_glyph_with_overflow_character(fonts, &job, last_row);
|
replace_last_glyph_with_overflow_character(fonts, &job, last_row);
|
||||||
if let Some(last) = last_row.glyphs.last() {
|
if let Some(last) = last_row.glyphs.last() {
|
||||||
last_row.rect.max.x = last.max_x();
|
last_row.size.x = last.max_x();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,12 +109,13 @@ pub fn layout(fonts: &mut FontsImpl, job: Arc<LayoutJob>) -> Galley {
|
|||||||
|
|
||||||
if justify || job.halign != Align::LEFT {
|
if justify || job.halign != Align::LEFT {
|
||||||
let num_rows = rows.len();
|
let num_rows = rows.len();
|
||||||
for (i, (row, _)) in rows.iter_mut().enumerate() {
|
for (i, placed_row) in rows.iter_mut().enumerate() {
|
||||||
let is_last_row = i + 1 == num_rows;
|
let is_last_row = i + 1 == num_rows;
|
||||||
let justify_row = justify && !row.ends_with_newline && !is_last_row;
|
let justify_row = justify && !placed_row.ends_with_newline && !is_last_row;
|
||||||
halign_and_justify_row(
|
halign_and_justify_row(
|
||||||
point_scale,
|
point_scale,
|
||||||
Arc::get_mut(row).unwrap(),
|
Arc::get_mut(&mut placed_row.row).unwrap(),
|
||||||
|
&mut placed_row.pos,
|
||||||
job.halign,
|
job.halign,
|
||||||
job.wrap.max_width,
|
job.wrap.max_width,
|
||||||
justify_row,
|
justify_row,
|
||||||
@@ -199,7 +200,7 @@ fn rows_from_paragraphs(
|
|||||||
paragraphs: Vec<Paragraph>,
|
paragraphs: Vec<Paragraph>,
|
||||||
job: &LayoutJob,
|
job: &LayoutJob,
|
||||||
elided: &mut bool,
|
elided: &mut bool,
|
||||||
) -> Vec<(Arc<Row>, Pos2)> {
|
) -> Vec<PlacedRow> {
|
||||||
let num_paragraphs = paragraphs.len();
|
let num_paragraphs = paragraphs.len();
|
||||||
|
|
||||||
let mut rows = vec![];
|
let mut rows = vec![];
|
||||||
@@ -213,38 +214,35 @@ fn rows_from_paragraphs(
|
|||||||
let is_last_paragraph = (i + 1) == num_paragraphs;
|
let is_last_paragraph = (i + 1) == num_paragraphs;
|
||||||
|
|
||||||
if paragraph.glyphs.is_empty() {
|
if paragraph.glyphs.is_empty() {
|
||||||
rows.push((
|
rows.push(PlacedRow {
|
||||||
Arc::new(Row {
|
row: Arc::new(Row {
|
||||||
section_index_at_start: paragraph.section_index_at_start,
|
section_index_at_start: paragraph.section_index_at_start,
|
||||||
glyphs: vec![],
|
glyphs: vec![],
|
||||||
visuals: Default::default(),
|
visuals: Default::default(),
|
||||||
rect: Rect::from_min_size(
|
size: vec2(0.0, paragraph.empty_paragraph_height),
|
||||||
pos2(paragraph.cursor_x, 0.0),
|
|
||||||
vec2(0.0, paragraph.empty_paragraph_height),
|
|
||||||
),
|
|
||||||
ends_with_newline: !is_last_paragraph,
|
|
||||||
}),
|
}),
|
||||||
Pos2::ZERO,
|
pos: pos2(paragraph.cursor_x, 0.0),
|
||||||
));
|
ends_with_newline: !is_last_paragraph,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
let paragraph_max_x = paragraph.glyphs.last().unwrap().max_x();
|
let paragraph_max_x = paragraph.glyphs.last().unwrap().max_x();
|
||||||
if paragraph_max_x <= job.effective_wrap_width() {
|
if paragraph_max_x <= job.effective_wrap_width() {
|
||||||
// Early-out optimization: the whole paragraph fits on one row.
|
// Early-out optimization: the whole paragraph fits on one row.
|
||||||
let paragraph_min_x = paragraph.glyphs[0].pos.x;
|
let paragraph_min_x = paragraph.glyphs[0].pos.x;
|
||||||
rows.push((
|
let rect = rect_from_x_range(paragraph_min_x..=paragraph_max_x);
|
||||||
Arc::new(Row {
|
rows.push(PlacedRow {
|
||||||
|
row: Arc::new(Row {
|
||||||
section_index_at_start: paragraph.section_index_at_start,
|
section_index_at_start: paragraph.section_index_at_start,
|
||||||
glyphs: paragraph.glyphs,
|
glyphs: paragraph.glyphs,
|
||||||
visuals: Default::default(),
|
visuals: Default::default(),
|
||||||
rect: rect_from_x_range(paragraph_min_x..=paragraph_max_x),
|
size: rect.size(),
|
||||||
ends_with_newline: !is_last_paragraph,
|
|
||||||
}),
|
}),
|
||||||
Pos2::ZERO,
|
pos: rect.min,
|
||||||
));
|
ends_with_newline: !is_last_paragraph,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
line_break(¶graph, job, &mut rows, elided);
|
line_break(¶graph, job, &mut rows, elided);
|
||||||
let last_row = Arc::get_mut(&mut rows.last_mut().unwrap().0).unwrap();
|
rows.last_mut().unwrap().ends_with_newline = !is_last_paragraph;
|
||||||
last_row.ends_with_newline = !is_last_paragraph;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,7 +253,7 @@ fn rows_from_paragraphs(
|
|||||||
fn line_break(
|
fn line_break(
|
||||||
paragraph: &Paragraph,
|
paragraph: &Paragraph,
|
||||||
job: &LayoutJob,
|
job: &LayoutJob,
|
||||||
out_rows: &mut Vec<(Arc<Row>, Pos2)>,
|
out_rows: &mut Vec<PlacedRow>,
|
||||||
elided: &mut bool,
|
elided: &mut bool,
|
||||||
) {
|
) {
|
||||||
let wrap_width = job.effective_wrap_width();
|
let wrap_width = job.effective_wrap_width();
|
||||||
@@ -283,16 +281,17 @@ fn line_break(
|
|||||||
{
|
{
|
||||||
// Allow the first row to be completely empty, because we know there will be more space on the next row:
|
// Allow the first row to be completely empty, because we know there will be more space on the next row:
|
||||||
// TODO(emilk): this records the height of this first row as zero, though that is probably fine since first_row_indentation usually comes with a first_row_min_height.
|
// TODO(emilk): this records the height of this first row as zero, though that is probably fine since first_row_indentation usually comes with a first_row_min_height.
|
||||||
out_rows.push((
|
let rect = rect_from_x_range(first_row_indentation..=first_row_indentation);
|
||||||
Arc::new(Row {
|
out_rows.push(PlacedRow {
|
||||||
|
row: Arc::new(Row {
|
||||||
section_index_at_start: paragraph.section_index_at_start,
|
section_index_at_start: paragraph.section_index_at_start,
|
||||||
glyphs: vec![],
|
glyphs: vec![],
|
||||||
visuals: Default::default(),
|
visuals: Default::default(),
|
||||||
rect: rect_from_x_range(first_row_indentation..=first_row_indentation),
|
size: rect.size(),
|
||||||
ends_with_newline: false,
|
|
||||||
}),
|
}),
|
||||||
Pos2::ZERO,
|
pos: rect.min,
|
||||||
));
|
ends_with_newline: false,
|
||||||
|
});
|
||||||
row_start_x += first_row_indentation;
|
row_start_x += first_row_indentation;
|
||||||
first_row_indentation = 0.0;
|
first_row_indentation = 0.0;
|
||||||
} else if let Some(last_kept_index) = row_break_candidates.get(job.wrap.break_anywhere)
|
} else if let Some(last_kept_index) = row_break_candidates.get(job.wrap.break_anywhere)
|
||||||
@@ -310,16 +309,17 @@ fn line_break(
|
|||||||
let paragraph_min_x = glyphs[0].pos.x;
|
let paragraph_min_x = glyphs[0].pos.x;
|
||||||
let paragraph_max_x = glyphs.last().unwrap().max_x();
|
let paragraph_max_x = glyphs.last().unwrap().max_x();
|
||||||
|
|
||||||
out_rows.push((
|
let rect = rect_from_x_range(paragraph_min_x..=paragraph_max_x);
|
||||||
Arc::new(Row {
|
out_rows.push(PlacedRow {
|
||||||
|
row: Arc::new(Row {
|
||||||
section_index_at_start,
|
section_index_at_start,
|
||||||
glyphs,
|
glyphs,
|
||||||
visuals: Default::default(),
|
visuals: Default::default(),
|
||||||
rect: rect_from_x_range(paragraph_min_x..=paragraph_max_x),
|
size: rect.size(),
|
||||||
ends_with_newline: false,
|
|
||||||
}),
|
}),
|
||||||
Pos2::ZERO,
|
pos: rect.min,
|
||||||
));
|
ends_with_newline: false,
|
||||||
|
});
|
||||||
|
|
||||||
// Start a new row:
|
// Start a new row:
|
||||||
row_start_idx = last_kept_index + 1;
|
row_start_idx = last_kept_index + 1;
|
||||||
@@ -352,16 +352,17 @@ fn line_break(
|
|||||||
let paragraph_min_x = glyphs[0].pos.x;
|
let paragraph_min_x = glyphs[0].pos.x;
|
||||||
let paragraph_max_x = glyphs.last().unwrap().max_x();
|
let paragraph_max_x = glyphs.last().unwrap().max_x();
|
||||||
|
|
||||||
out_rows.push((
|
let rect = rect_from_x_range(paragraph_min_x..=paragraph_max_x);
|
||||||
Arc::new(Row {
|
out_rows.push(PlacedRow {
|
||||||
|
row: Arc::new(Row {
|
||||||
section_index_at_start,
|
section_index_at_start,
|
||||||
glyphs,
|
glyphs,
|
||||||
visuals: Default::default(),
|
visuals: Default::default(),
|
||||||
rect: rect_from_x_range(paragraph_min_x..=paragraph_max_x),
|
size: rect.size(),
|
||||||
ends_with_newline: false,
|
|
||||||
}),
|
}),
|
||||||
Pos2::ZERO,
|
pos: rect.min,
|
||||||
));
|
ends_with_newline: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -523,6 +524,7 @@ fn replace_last_glyph_with_overflow_character(
|
|||||||
fn halign_and_justify_row(
|
fn halign_and_justify_row(
|
||||||
point_scale: PointScale,
|
point_scale: PointScale,
|
||||||
row: &mut Row,
|
row: &mut Row,
|
||||||
|
pos: &mut Pos2,
|
||||||
halign: Align,
|
halign: Align,
|
||||||
wrap_width: f32,
|
wrap_width: f32,
|
||||||
justify: bool,
|
justify: bool,
|
||||||
@@ -606,24 +608,25 @@ fn halign_and_justify_row(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Note we ignore the leading/trailing whitespace here!
|
// Note we ignore the leading/trailing whitespace here!
|
||||||
row.rect.min.x = target_min_x;
|
pos.x = target_min_x;
|
||||||
row.rect.max.x = target_max_x;
|
row.size.x = target_max_x - target_min_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the Y positions and tessellate the text.
|
/// Calculate the Y positions and tessellate the text.
|
||||||
fn galley_from_rows(
|
fn galley_from_rows(
|
||||||
point_scale: PointScale,
|
point_scale: PointScale,
|
||||||
job: Arc<LayoutJob>,
|
job: Arc<LayoutJob>,
|
||||||
mut rows: Vec<(Arc<Row>, Pos2)>,
|
mut rows: Vec<PlacedRow>,
|
||||||
elided: bool,
|
elided: bool,
|
||||||
) -> Galley {
|
) -> Galley {
|
||||||
let mut first_row_min_height = job.first_row_min_height;
|
let mut first_row_min_height = job.first_row_min_height;
|
||||||
let mut cursor_y = 0.0;
|
let mut cursor_y = 0.0;
|
||||||
let mut min_x: f32 = 0.0;
|
let mut min_x: f32 = 0.0;
|
||||||
let mut max_x: f32 = 0.0;
|
let mut max_x: f32 = 0.0;
|
||||||
for (row, _) in &mut rows {
|
for placed_row in &mut rows {
|
||||||
let row = Arc::get_mut(row).unwrap();
|
let mut max_row_height = first_row_min_height.max(placed_row.rect().height());
|
||||||
let mut max_row_height = first_row_min_height.max(row.rect.height());
|
let row = Arc::get_mut(&mut placed_row.row).unwrap();
|
||||||
|
|
||||||
first_row_min_height = 0.0;
|
first_row_min_height = 0.0;
|
||||||
for glyph in &row.glyphs {
|
for glyph in &row.glyphs {
|
||||||
max_row_height = max_row_height.max(glyph.line_height);
|
max_row_height = max_row_height.max(glyph.line_height);
|
||||||
@@ -634,8 +637,7 @@ fn galley_from_rows(
|
|||||||
for glyph in &mut row.glyphs {
|
for glyph in &mut row.glyphs {
|
||||||
let format = &job.sections[glyph.section_index as usize].format;
|
let format = &job.sections[glyph.section_index as usize].format;
|
||||||
|
|
||||||
glyph.pos.y = cursor_y
|
glyph.pos.y = glyph.font_impl_ascent
|
||||||
+ glyph.font_impl_ascent
|
|
||||||
|
|
||||||
// Apply valign to the different in height of the entire row, and the height of this `Font`:
|
// Apply valign to the different in height of the entire row, and the height of this `Font`:
|
||||||
+ format.valign.to_factor() * (max_row_height - glyph.line_height)
|
+ format.valign.to_factor() * (max_row_height - glyph.line_height)
|
||||||
@@ -644,14 +646,16 @@ fn galley_from_rows(
|
|||||||
// we always center the difference:
|
// we always center the difference:
|
||||||
+ 0.5 * (glyph.font_height - glyph.font_impl_height);
|
+ 0.5 * (glyph.font_height - glyph.font_impl_height);
|
||||||
|
|
||||||
glyph.pos.y = point_scale.round_to_pixel(glyph.pos.y);
|
// FIXME(afishhh): HACK! change the proper code above instead!!
|
||||||
|
// this should probably not be merged like this!
|
||||||
|
glyph.pos.x -= placed_row.pos.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
row.rect.min.y = cursor_y;
|
placed_row.pos.y = cursor_y;
|
||||||
row.rect.max.y = cursor_y + max_row_height;
|
row.size.y = max_row_height;
|
||||||
|
|
||||||
min_x = min_x.min(row.rect.min.x);
|
min_x = min_x.min(placed_row.rect().min.x);
|
||||||
max_x = max_x.max(row.rect.max.x);
|
max_x = max_x.max(placed_row.rect().max.x);
|
||||||
cursor_y += max_row_height;
|
cursor_y += max_row_height;
|
||||||
cursor_y = point_scale.round_to_pixel(cursor_y);
|
cursor_y = point_scale.round_to_pixel(cursor_y);
|
||||||
}
|
}
|
||||||
@@ -662,8 +666,8 @@ fn galley_from_rows(
|
|||||||
let mut num_vertices = 0;
|
let mut num_vertices = 0;
|
||||||
let mut num_indices = 0;
|
let mut num_indices = 0;
|
||||||
|
|
||||||
for (row, _) in &mut rows {
|
for placed_row in &mut rows {
|
||||||
let row = Arc::get_mut(row).unwrap();
|
let row = Arc::get_mut(&mut placed_row.row).unwrap();
|
||||||
row.visuals = tessellate_row(point_scale, &job, &format_summary, row);
|
row.visuals = tessellate_row(point_scale, &job, &format_summary, row);
|
||||||
mesh_bounds = mesh_bounds.union(row.visuals.mesh_bounds);
|
mesh_bounds = mesh_bounds.union(row.visuals.mesh_bounds);
|
||||||
num_vertices += row.visuals.mesh.vertices.len();
|
num_vertices += row.visuals.mesh.vertices.len();
|
||||||
@@ -1096,7 +1100,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(galley.elided);
|
assert!(galley.elided);
|
||||||
assert_eq!(galley.rows.len(), 1);
|
assert_eq!(galley.rows.len(), 1);
|
||||||
let row_text = galley.rows[0].0.text();
|
let row_text = galley.rows[0].text();
|
||||||
assert!(
|
assert!(
|
||||||
row_text.ends_with('…'),
|
row_text.ends_with('…'),
|
||||||
"Expected row to end with `…`, got {row_text:?} when line-breaking the text {text:?} with max_width {max_width} and break_anywhere {break_anywhere}.",
|
"Expected row to end with `…`, got {row_text:?} when line-breaking the text {text:?} with max_width {max_width} and break_anywhere {break_anywhere}.",
|
||||||
@@ -1115,7 +1119,7 @@ mod tests {
|
|||||||
|
|
||||||
assert!(galley.elided);
|
assert!(galley.elided);
|
||||||
assert_eq!(galley.rows.len(), 1);
|
assert_eq!(galley.rows.len(), 1);
|
||||||
let row_text = galley.rows[0].0.text();
|
let row_text = galley.rows[0].text();
|
||||||
assert_eq!(row_text, "Hello…");
|
assert_eq!(row_text, "Hello…");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1130,11 +1134,7 @@ mod tests {
|
|||||||
layout_job.wrap.max_width = 90.0;
|
layout_job.wrap.max_width = 90.0;
|
||||||
let galley = layout(&mut fonts, layout_job.into());
|
let galley = layout(&mut fonts, layout_job.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
galley
|
galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.map(|row| row.0.text())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
vec!["日本語と", "Englishの混在", "した文章"]
|
vec!["日本語と", "Englishの混在", "した文章"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1149,11 +1149,7 @@ mod tests {
|
|||||||
layout_job.wrap.max_width = 110.0;
|
layout_job.wrap.max_width = 110.0;
|
||||||
let galley = layout(&mut fonts, layout_job.into());
|
let galley = layout(&mut fonts, layout_job.into());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
galley
|
galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.map(|row| row.0.text())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
vec!["日本語とEnglish", "の混在した文章"]
|
vec!["日本語とEnglish", "の混在した文章"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1168,14 +1164,11 @@ mod tests {
|
|||||||
let galley = layout(&mut fonts, layout_job.into());
|
let galley = layout(&mut fonts, layout_job.into());
|
||||||
assert!(galley.elided);
|
assert!(galley.elided);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
galley
|
galley.rows.iter().map(|row| row.text()).collect::<Vec<_>>(),
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.map(|row| row.0.text())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
vec!["# DNA…"]
|
vec!["# DNA…"]
|
||||||
);
|
);
|
||||||
let row = &galley.rows[0];
|
let row = &galley.rows[0];
|
||||||
assert_eq!(row.0.rect.max.x, row.0.glyphs.last().unwrap().max_x());
|
assert_eq!(row.pos, Pos2::ZERO);
|
||||||
|
assert_eq!(row.rect().max.x, row.glyphs.last().unwrap().max_x());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ pub struct Galley {
|
|||||||
///
|
///
|
||||||
/// Note that a paragraph (a piece of text separated with `\n`)
|
/// Note that a paragraph (a piece of text separated with `\n`)
|
||||||
/// can be split up into multiple rows.
|
/// can be split up into multiple rows.
|
||||||
pub rows: Vec<(Arc<Row>, Pos2)>,
|
pub rows: Vec<PlacedRow>,
|
||||||
|
|
||||||
/// Set to true the text was truncated due to [`TextWrapping::max_rows`].
|
/// Set to true the text was truncated due to [`TextWrapping::max_rows`].
|
||||||
pub elided: bool,
|
pub elided: bool,
|
||||||
@@ -538,6 +538,39 @@ pub struct Galley {
|
|||||||
pub pixels_per_point: f32,
|
pub pixels_per_point: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct PlacedRow {
|
||||||
|
/// The underlying row unpositioned [`Row`].
|
||||||
|
pub row: Arc<Row>,
|
||||||
|
|
||||||
|
/// The position of this [`Row`] relative to the galley.
|
||||||
|
pub pos: Pos2,
|
||||||
|
|
||||||
|
/// If true, this [`PlacedRow`] came from a paragraph ending with a `\n`.
|
||||||
|
/// The `\n` itself is omitted from [`Row::glyphs`].
|
||||||
|
/// A `\n` in the input text always creates a new [`PlacedRow`] below it,
|
||||||
|
/// so that text that ends with `\n` has an empty [`PlacedRow`] last.
|
||||||
|
/// This also implies that the last [`PlacedRow`] in a [`Galley`] always has `ends_with_newline == false`.
|
||||||
|
pub ends_with_newline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlacedRow {
|
||||||
|
/// Logical bounding rectangle on font heights etc.
|
||||||
|
/// Use this when drawing a selection or similar!
|
||||||
|
pub fn rect(&self) -> Rect {
|
||||||
|
Rect::from_min_size(self.pos, self.row.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for PlacedRow {
|
||||||
|
type Target = Row;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
@@ -547,20 +580,12 @@ pub struct Row {
|
|||||||
/// One for each `char`.
|
/// One for each `char`.
|
||||||
pub glyphs: Vec<Glyph>,
|
pub glyphs: Vec<Glyph>,
|
||||||
|
|
||||||
/// Logical bounding rectangle based on font heights etc.
|
/// Logical size based on font heights etc.
|
||||||
/// Use this when drawing a selection or similar!
|
|
||||||
/// Includes leading and trailing whitespace.
|
/// Includes leading and trailing whitespace.
|
||||||
pub rect: Rect,
|
pub size: Vec2,
|
||||||
|
|
||||||
/// The mesh, ready to be rendered.
|
/// The mesh, ready to be rendered.
|
||||||
pub visuals: RowVisuals,
|
pub visuals: RowVisuals,
|
||||||
|
|
||||||
/// If true, this [`Row`] came from a paragraph ending with a `\n`.
|
|
||||||
/// The `\n` itself is omitted from [`Self::glyphs`].
|
|
||||||
/// A `\n` in the input text always creates a new [`Row`] below it,
|
|
||||||
/// so that text that ends with `\n` has an empty [`Row`] last.
|
|
||||||
/// This also implies that the last [`Row`] in a [`Galley`] always has `ends_with_newline == false`.
|
|
||||||
pub ends_with_newline: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tessellated output of a row.
|
/// The tessellated output of a row.
|
||||||
@@ -668,28 +693,7 @@ impl Row {
|
|||||||
self.glyphs.len()
|
self.glyphs.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Includes the implicit `\n` after the [`Row`], if any.
|
/// Closest char at the desired x coordinate in row-relative coordinates.
|
||||||
#[inline]
|
|
||||||
pub fn char_count_including_newline(&self) -> usize {
|
|
||||||
self.glyphs.len() + (self.ends_with_newline as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn min_y(&self) -> f32 {
|
|
||||||
self.rect.top()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn max_y(&self) -> f32 {
|
|
||||||
self.rect.bottom()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn height(&self) -> f32 {
|
|
||||||
self.rect.height()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Closest char at the desired x coordinate.
|
|
||||||
/// Returns something in the range `[0, char_count_excluding_newline()]`.
|
/// Returns something in the range `[0, char_count_excluding_newline()]`.
|
||||||
pub fn char_at(&self, desired_x: f32) -> usize {
|
pub fn char_at(&self, desired_x: f32) -> usize {
|
||||||
for (i, glyph) in self.glyphs.iter().enumerate() {
|
for (i, glyph) in self.glyphs.iter().enumerate() {
|
||||||
@@ -704,11 +708,34 @@ impl Row {
|
|||||||
if let Some(glyph) = self.glyphs.get(column) {
|
if let Some(glyph) = self.glyphs.get(column) {
|
||||||
glyph.pos.x
|
glyph.pos.x
|
||||||
} else {
|
} else {
|
||||||
self.rect.right()
|
self.size.x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PlacedRow {
|
||||||
|
/// Includes the implicit `\n` after the [`Row`], if any.
|
||||||
|
#[inline]
|
||||||
|
pub fn char_count_including_newline(&self) -> usize {
|
||||||
|
self.glyphs.len() + (self.ends_with_newline as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn min_y(&self) -> f32 {
|
||||||
|
self.rect().top()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn max_y(&self) -> f32 {
|
||||||
|
self.rect().bottom()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn height(&self) -> f32 {
|
||||||
|
self.row.size.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Galley {
|
impl Galley {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
@@ -755,8 +782,8 @@ impl std::ops::Deref for Galley {
|
|||||||
impl Galley {
|
impl Galley {
|
||||||
/// Zero-width rect past the last character.
|
/// Zero-width rect past the last character.
|
||||||
fn end_pos(&self) -> Rect {
|
fn end_pos(&self) -> Rect {
|
||||||
if let Some((row, _)) = self.rows.last() {
|
if let Some(row) = self.rows.last() {
|
||||||
let x = row.rect.right();
|
let x = row.rect().right();
|
||||||
Rect::from_min_max(pos2(x, row.min_y()), pos2(x, row.max_y()))
|
Rect::from_min_max(pos2(x, row.min_y()), pos2(x, row.max_y()))
|
||||||
} else {
|
} else {
|
||||||
// Empty galley
|
// Empty galley
|
||||||
@@ -773,7 +800,7 @@ impl Galley {
|
|||||||
pub fn pos_from_pcursor(&self, pcursor: PCursor) -> Rect {
|
pub fn pos_from_pcursor(&self, pcursor: PCursor) -> Rect {
|
||||||
let mut it = PCursor::default();
|
let mut it = PCursor::default();
|
||||||
|
|
||||||
for (row, offset) in &self.rows {
|
for row in &self.rows {
|
||||||
if it.paragraph == pcursor.paragraph {
|
if it.paragraph == pcursor.paragraph {
|
||||||
// Right paragraph, but is it the right row in the paragraph?
|
// Right paragraph, but is it the right row in the paragraph?
|
||||||
|
|
||||||
@@ -787,11 +814,8 @@ impl Galley {
|
|||||||
&& !row.ends_with_newline
|
&& !row.ends_with_newline
|
||||||
&& column >= row.char_count_excluding_newline();
|
&& column >= row.char_count_excluding_newline();
|
||||||
if !select_next_row_instead {
|
if !select_next_row_instead {
|
||||||
let x = row.x_offset(column) + offset.x;
|
let x = row.x_offset(column);
|
||||||
return Rect::from_min_max(
|
return Rect::from_min_max(pos2(x, row.min_y()), pos2(x, row.max_y()));
|
||||||
pos2(x, row.min_y() + offset.y),
|
|
||||||
pos2(x, row.max_y() + offset.y),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -825,13 +849,13 @@ impl Galley {
|
|||||||
/// same as a cursor at the end.
|
/// same as a cursor at the end.
|
||||||
/// This allows implementing text-selection by dragging above/below the galley.
|
/// This allows implementing text-selection by dragging above/below the galley.
|
||||||
pub fn cursor_from_pos(&self, pos: Vec2) -> Cursor {
|
pub fn cursor_from_pos(&self, pos: Vec2) -> Cursor {
|
||||||
if let Some((first_row, offset)) = self.rows.first() {
|
if let Some(first_row) = self.rows.first() {
|
||||||
if pos.y < first_row.min_y() + offset.y {
|
if pos.y < first_row.min_y() {
|
||||||
return self.begin();
|
return self.begin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((last_row, offset)) = self.rows.last() {
|
if let Some(last_row) = self.rows.last() {
|
||||||
if last_row.max_y() + offset.y < pos.y {
|
if last_row.max_y() < pos.y {
|
||||||
return self.end();
|
return self.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -842,15 +866,16 @@ impl Galley {
|
|||||||
let mut ccursor_index = 0;
|
let mut ccursor_index = 0;
|
||||||
let mut pcursor_it = PCursor::default();
|
let mut pcursor_it = PCursor::default();
|
||||||
|
|
||||||
for (row_nr, (row, offset)) in self.rows.iter().enumerate() {
|
for (row_nr, row) in self.rows.iter().enumerate() {
|
||||||
let min_y = row.min_y() + offset.y;
|
let min_y = row.min_y();
|
||||||
let max_y = row.max_y() + offset.y;
|
let max_y = row.max_y();
|
||||||
|
|
||||||
let is_pos_within_row = min_y <= pos.y && pos.y <= max_y;
|
let is_pos_within_row = min_y <= pos.y && pos.y <= max_y;
|
||||||
let y_dist = (min_y - pos.y).abs().min((max_y - pos.y).abs());
|
let y_dist = (min_y - pos.y).abs().min((max_y - pos.y).abs());
|
||||||
if is_pos_within_row || y_dist < best_y_dist {
|
if is_pos_within_row || y_dist < best_y_dist {
|
||||||
best_y_dist = y_dist;
|
best_y_dist = y_dist;
|
||||||
let column = row.char_at(pos.x);
|
// char_at is `Row` not `PlacedRow` relative which means we have to subtract the pos.
|
||||||
|
let column = row.char_at(pos.x - row.pos.x);
|
||||||
let prefer_next_row = column < row.char_count_excluding_newline();
|
let prefer_next_row = column < row.char_count_excluding_newline();
|
||||||
cursor = Cursor {
|
cursor = Cursor {
|
||||||
ccursor: CCursor {
|
ccursor: CCursor {
|
||||||
@@ -910,7 +935,7 @@ impl Galley {
|
|||||||
offset: 0,
|
offset: 0,
|
||||||
prefer_next_row: true,
|
prefer_next_row: true,
|
||||||
};
|
};
|
||||||
for (row, _) in &self.rows {
|
for row in &self.rows {
|
||||||
let row_char_count = row.char_count_including_newline();
|
let row_char_count = row.char_count_including_newline();
|
||||||
ccursor.index += row_char_count;
|
ccursor.index += row_char_count;
|
||||||
if row.ends_with_newline {
|
if row.ends_with_newline {
|
||||||
@@ -928,7 +953,7 @@ impl Galley {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_rcursor(&self) -> RCursor {
|
pub fn end_rcursor(&self) -> RCursor {
|
||||||
if let Some((last_row, _)) = self.rows.last() {
|
if let Some(last_row) = self.rows.last() {
|
||||||
RCursor {
|
RCursor {
|
||||||
row: self.rows.len() - 1,
|
row: self.rows.len() - 1,
|
||||||
column: last_row.char_count_including_newline(),
|
column: last_row.char_count_including_newline(),
|
||||||
@@ -954,7 +979,7 @@ impl Galley {
|
|||||||
prefer_next_row,
|
prefer_next_row,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (row_nr, (row, _)) in self.rows.iter().enumerate() {
|
for (row_nr, row) in self.rows.iter().enumerate() {
|
||||||
let row_char_count = row.char_count_excluding_newline();
|
let row_char_count = row.char_count_excluding_newline();
|
||||||
|
|
||||||
if ccursor_it.index <= ccursor.index
|
if ccursor_it.index <= ccursor.index
|
||||||
@@ -999,7 +1024,7 @@ impl Galley {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let prefer_next_row =
|
let prefer_next_row =
|
||||||
rcursor.column < self.rows[rcursor.row].0.char_count_excluding_newline();
|
rcursor.column < self.rows[rcursor.row].char_count_excluding_newline();
|
||||||
let mut ccursor_it = CCursor {
|
let mut ccursor_it = CCursor {
|
||||||
index: 0,
|
index: 0,
|
||||||
prefer_next_row,
|
prefer_next_row,
|
||||||
@@ -1010,7 +1035,7 @@ impl Galley {
|
|||||||
prefer_next_row,
|
prefer_next_row,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (row_nr, (row, _)) in self.rows.iter().enumerate() {
|
for (row_nr, row) in self.rows.iter().enumerate() {
|
||||||
if row_nr == rcursor.row {
|
if row_nr == rcursor.row {
|
||||||
ccursor_it.index += rcursor.column.at_most(row.char_count_excluding_newline());
|
ccursor_it.index += rcursor.column.at_most(row.char_count_excluding_newline());
|
||||||
|
|
||||||
@@ -1054,7 +1079,7 @@ impl Galley {
|
|||||||
prefer_next_row,
|
prefer_next_row,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (row_nr, (row, _)) in self.rows.iter().enumerate() {
|
for (row_nr, row) in self.rows.iter().enumerate() {
|
||||||
if pcursor_it.paragraph == pcursor.paragraph {
|
if pcursor_it.paragraph == pcursor.paragraph {
|
||||||
// Right paragraph, but is it the right row in the paragraph?
|
// Right paragraph, but is it the right row in the paragraph?
|
||||||
|
|
||||||
@@ -1128,9 +1153,7 @@ impl Galley {
|
|||||||
let new_row = cursor.rcursor.row - 1;
|
let new_row = cursor.rcursor.row - 1;
|
||||||
|
|
||||||
let cursor_is_beyond_end_of_current_row = cursor.rcursor.column
|
let cursor_is_beyond_end_of_current_row = cursor.rcursor.column
|
||||||
>= self.rows[cursor.rcursor.row]
|
>= self.rows[cursor.rcursor.row].char_count_excluding_newline();
|
||||||
.0
|
|
||||||
.char_count_excluding_newline();
|
|
||||||
|
|
||||||
let new_rcursor = if cursor_is_beyond_end_of_current_row {
|
let new_rcursor = if cursor_is_beyond_end_of_current_row {
|
||||||
// keep same column
|
// keep same column
|
||||||
@@ -1141,8 +1164,8 @@ impl Galley {
|
|||||||
} else {
|
} else {
|
||||||
// keep same X coord
|
// keep same X coord
|
||||||
let x = self.pos_from_cursor(cursor).center().x;
|
let x = self.pos_from_cursor(cursor).center().x;
|
||||||
let (row, offset) = &self.rows[new_row];
|
let row = &self.rows[new_row];
|
||||||
let column = if x > row.rect.right() + offset.x {
|
let column = if x > row.rect().right() {
|
||||||
// beyond the end of this row - keep same column
|
// beyond the end of this row - keep same column
|
||||||
cursor.rcursor.column
|
cursor.rcursor.column
|
||||||
} else {
|
} else {
|
||||||
@@ -1162,9 +1185,7 @@ impl Galley {
|
|||||||
let new_row = cursor.rcursor.row + 1;
|
let new_row = cursor.rcursor.row + 1;
|
||||||
|
|
||||||
let cursor_is_beyond_end_of_current_row = cursor.rcursor.column
|
let cursor_is_beyond_end_of_current_row = cursor.rcursor.column
|
||||||
>= self.rows[cursor.rcursor.row]
|
>= self.rows[cursor.rcursor.row].char_count_excluding_newline();
|
||||||
.0
|
|
||||||
.char_count_excluding_newline();
|
|
||||||
|
|
||||||
let new_rcursor = if cursor_is_beyond_end_of_current_row {
|
let new_rcursor = if cursor_is_beyond_end_of_current_row {
|
||||||
// keep same column
|
// keep same column
|
||||||
@@ -1175,8 +1196,8 @@ impl Galley {
|
|||||||
} else {
|
} else {
|
||||||
// keep same X coord
|
// keep same X coord
|
||||||
let x = self.pos_from_cursor(cursor).center().x;
|
let x = self.pos_from_cursor(cursor).center().x;
|
||||||
let (row, offset) = &self.rows[new_row];
|
let row = &self.rows[new_row];
|
||||||
let column = if x > row.rect.right() + offset.x {
|
let column = if x > row.rect().right() {
|
||||||
// beyond the end of the next row - keep same column
|
// beyond the end of the next row - keep same column
|
||||||
cursor.rcursor.column
|
cursor.rcursor.column
|
||||||
} else {
|
} else {
|
||||||
@@ -1204,9 +1225,7 @@ impl Galley {
|
|||||||
pub fn cursor_end_of_row(&self, cursor: &Cursor) -> Cursor {
|
pub fn cursor_end_of_row(&self, cursor: &Cursor) -> Cursor {
|
||||||
self.from_rcursor(RCursor {
|
self.from_rcursor(RCursor {
|
||||||
row: cursor.rcursor.row,
|
row: cursor.rcursor.row,
|
||||||
column: self.rows[cursor.rcursor.row]
|
column: self.rows[cursor.rcursor.row].char_count_excluding_newline(),
|
||||||
.0
|
|
||||||
.char_count_excluding_newline(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user