Skip to content

Commit

Permalink
Fix glitches when scrolling
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Aug 27, 2024
1 parent cee020e commit fb034dd
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 29 deletions.
5 changes: 5 additions & 0 deletions crates/egui/src/layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ impl PaintList {
self.0[idx.0].shape = Shape::Noop;
}

/// Mutate the shape at the given index, if any.
pub fn mutate_shape(&mut self, idx: ShapeIdx, f: impl FnOnce(&mut ClippedShape)) {
self.0.get_mut(idx.0).map(f);
}

/// Transform each [`Shape`] and clip rectangle by this much, in-place
pub fn transform(&mut self, transform: TSTransform) {
for ClippedShape { clip_rect, shape } in &mut self.0 {
Expand Down
63 changes: 51 additions & 12 deletions crates/egui/src/text_selection/label_text_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::{
};

use super::{
text_cursor_state::cursor_rect, visuals::paint_text_selection, CursorRange, TextCursorState,
text_cursor_state::cursor_rect,
visuals::{paint_text_selection, RowVertexIndices},
CursorRange, TextCursorState,
};

/// Turn on to help debug this
Expand Down Expand Up @@ -92,7 +94,9 @@ pub struct LabelSelectionState {
last_copied_galley_rect: Option<Rect>,

/// Painted selections this frame.
painted_shape_idx: Vec<ShapeIdx>,
///
/// Kept so we can undo a bad selection visualization if we don't see both ends of the selection this frame.
painted_selections: Vec<(ShapeIdx, Vec<RowVertexIndices>)>,
}

impl Default for LabelSelectionState {
Expand All @@ -107,7 +111,7 @@ impl Default for LabelSelectionState {
has_reached_secondary: Default::default(),
text_to_copy: Default::default(),
last_copied_galley_rect: Default::default(),
painted_shape_idx: Default::default(),
painted_selections: Default::default(),
}
}
}
Expand Down Expand Up @@ -150,7 +154,7 @@ impl LabelSelectionState {
state.has_reached_secondary = false;
state.text_to_copy.clear();
state.last_copied_galley_rect = None;
state.painted_shape_idx.clear();
state.painted_selections.clear();

state.store(ctx);
}
Expand All @@ -173,8 +177,26 @@ impl LabelSelectionState {
// glitching by removing all painted selections:
ctx.graphics_mut(|layers| {
if let Some(list) = layers.get_mut(selection.layer_id) {
for shape_idx in state.painted_shape_idx.drain(..) {
list.reset_shape(shape_idx);
for (shape_idx, row_selections) in state.painted_selections.drain(..) {
list.mutate_shape(shape_idx, |shape| {
if let epaint::Shape::Text(text_shape) = &mut shape.shape {
let galley = Arc::make_mut(&mut text_shape.galley);
for row_selection in row_selections {
if let Some(row) = galley.rows.get_mut(row_selection.row) {
for vertex_index in row_selection.vertex_indices {
if let Some(vertex) = row
.visuals
.mesh
.vertices
.get_mut(vertex_index as usize)
{
vertex.color = epaint::Color32::TRANSPARENT;
}
}
}
}
}
});
}
}
});
Expand Down Expand Up @@ -260,16 +282,28 @@ impl LabelSelectionState {
///
/// Make sure the widget senses clicks and drags.
///
/// This should be called before painting the text, because this will
/// add the text selection (if any) to the galley.
/// This also takes care of painting the galley.
pub fn label_text_selection(
ui: &Ui,
response: &Response,
galley_pos: Pos2,
galley: &mut Arc<Galley>,
mut galley: Arc<Galley>,
fallback_color: epaint::Color32,
underline: epaint::Stroke,
) {
let mut state = Self::load(ui.ctx());
state.on_label(ui, response, galley_pos, galley);
let new_vertex_indices = state.on_label(ui, response, galley_pos, &mut galley);

let shape_idx = ui.painter().add(
epaint::TextShape::new(galley_pos, galley, fallback_color).with_underline(underline),
);

if !new_vertex_indices.is_empty() {
state
.painted_selections
.push((shape_idx, new_vertex_indices));
}

state.store(ui.ctx());
}

Expand Down Expand Up @@ -443,13 +477,14 @@ impl LabelSelectionState {
}
}

/// Returns indices of new vertices in the galley, if any.
fn on_label(
&mut self,
ui: &Ui,
response: &Response,
galley_pos: Pos2,
galley: &mut Arc<Galley>,
) {
) -> Vec<RowVertexIndices> {
let widget_id = response.id;

if response.hovered {
Expand Down Expand Up @@ -557,12 +592,14 @@ impl LabelSelectionState {

let cursor_range = cursor_state.range(galley);

let mut new_vertex_indices = vec![];

if let Some(cursor_range) = cursor_range {
paint_text_selection(
galley,
ui.visuals(),
&cursor_range,
Some(&mut self.painted_shape_idx),
Some(&mut new_vertex_indices),
);
}

Expand All @@ -575,6 +612,8 @@ impl LabelSelectionState {
galley_pos,
galley,
);

new_vertex_indices
}
}

Expand Down
17 changes: 12 additions & 5 deletions crates/egui/src/text_selection/visuals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ use std::sync::Arc;

use crate::*;

use self::layers::ShapeIdx;

use super::CursorRange;

#[derive(Clone, Debug)]
pub struct RowVertexIndices {
pub row: usize,
pub vertex_indices: [u32; 6],
}

/// Adds text selection rectangles to the galley.
pub fn paint_text_selection(
galley: &mut Arc<Galley>,
visuals: &Visuals,
cursor_range: &CursorRange,
mut out_shaped_idx: Option<&mut Vec<ShapeIdx>>,
mut new_vertex_indices: Option<&mut Vec<RowVertexIndices>>,
) {
if cursor_range.is_empty() {
return;
Expand Down Expand Up @@ -75,8 +79,11 @@ pub fn paint_text_selection(
mesh.indices[glyph_index_start..glyph_index_start + 6]
.clone_from_slice(&selection_triangles);

if let Some(out_shaped_idx) = &mut out_shaped_idx {
// TODO
if let Some(new_vertex_indices) = &mut new_vertex_indices {
new_vertex_indices.push(RowVertexIndices {
row: ri,
vertex_indices: selection_triangles,
});
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions crates/egui/src/widgets/hyperlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl Widget for Link {
let Self { text } = self;
let label = Label::new(text).sense(Sense::click());

let (galley_pos, mut galley, response) = label.layout_in_ui(ui);
let (galley_pos, galley, response) = label.layout_in_ui(ui);
response
.widget_info(|| WidgetInfo::labeled(WidgetType::Link, ui.is_enabled(), galley.text()));

Expand All @@ -52,12 +52,15 @@ impl Widget for Link {

let selectable = ui.style().interaction.selectable_labels;
if selectable {
LabelSelectionState::label_text_selection(ui, &response, galley_pos, &mut galley);
LabelSelectionState::label_text_selection(
ui, &response, galley_pos, galley, color, underline,
);
} else {
ui.painter().add(
epaint::TextShape::new(galley_pos, galley, color).with_underline(underline),
);
}

ui.painter()
.add(epaint::TextShape::new(galley_pos, galley, color).with_underline(underline));

if response.hovered() {
ui.ctx().set_cursor_icon(CursorIcon::PointingHand);
}
Expand Down
21 changes: 14 additions & 7 deletions crates/egui/src/widgets/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ impl Widget for Label {

let selectable = self.selectable;

let (galley_pos, mut galley, mut response) = self.layout_in_ui(ui);
let (galley_pos, galley, mut response) = self.layout_in_ui(ui);
response
.widget_info(|| WidgetInfo::labeled(WidgetType::Label, ui.is_enabled(), galley.text()));

Expand All @@ -269,13 +269,20 @@ impl Widget for Label {

let selectable = selectable.unwrap_or_else(|| ui.style().interaction.selectable_labels);
if selectable {
LabelSelectionState::label_text_selection(ui, &response, galley_pos, &mut galley);
LabelSelectionState::label_text_selection(
ui,
&response,
galley_pos,
galley,
response_color,
underline,
);
} else {
ui.painter().add(
epaint::TextShape::new(galley_pos, galley, response_color)
.with_underline(underline),
);
}

ui.painter().add(
epaint::TextShape::new(galley_pos, galley, response_color)
.with_underline(underline),
);
}

response
Expand Down

0 comments on commit fb034dd

Please sign in to comment.