Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross-widget text select #3870

Merged
merged 7 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ impl Context {
/// ```
pub fn begin_frame(&self, new_input: RawInput) {
crate::profile_function!();

crate::text_selection::LabelSelectionState::begin_frame(self);
self.write(|ctx| ctx.begin_frame_mut(new_input));
}
}
Expand Down Expand Up @@ -697,13 +697,13 @@ impl Context {

/// Read-write access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.
#[inline]
pub(crate) fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
pub fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
self.write(move |ctx| writer(&mut ctx.viewport().graphics))
}

/// Read-only access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.
#[inline]
pub(crate) fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {
pub fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {
self.write(move |ctx| reader(&ctx.viewport().graphics))
}

Expand Down Expand Up @@ -1616,6 +1616,8 @@ impl Context {
crate::gui_zoom::zoom_with_keyboard(self);
}

crate::text_selection::LabelSelectionState::end_frame(self);

let debug_texts = self.write(|ctx| std::mem::take(&mut ctx.debug_texts));
if !debug_texts.is_empty() {
// Show debug-text next to the cursor.
Expand Down Expand Up @@ -2041,7 +2043,7 @@ impl Context {
/// Can be used to implement drag-and-drop (see relevant demo).
pub fn translate_layer(&self, layer_id: LayerId, delta: Vec2) {
if delta != Vec2::ZERO {
self.graphics_mut(|g| g.list(layer_id).translate(delta));
self.graphics_mut(|g| g.entry(layer_id).translate(delta));
}
}

Expand Down Expand Up @@ -2352,6 +2354,15 @@ impl Context {
let font_image_size = self.fonts(|f| f.font_image_size());
crate::introspection::font_texture_ui(ui, font_image_size);
});

CollapsingHeader::new("Label text selection state")
.default_open(false)
.show(ui, |ui| {
ui.label(format!(
"{:#?}",
crate::text_selection::LabelSelectionState::load(ui.ctx())
));
});
}

/// Show stats about the allocated textures.
Expand Down
1 change: 1 addition & 0 deletions crates/egui/src/input_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,7 @@ impl PointerState {
}

/// Was any pointer button pressed (`!down -> down`) this frame?
///
/// This can sometimes return `true` even if `any_down() == false`
/// because a press can be shorted than one frame.
pub fn any_pressed(&self) -> bool {
Expand Down
21 changes: 18 additions & 3 deletions crates/egui/src/layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ impl LayerId {
}

/// A unique identifier of a specific [`Shape`] in a [`PaintList`].
#[derive(Clone, Copy, PartialEq, Eq)]

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ShapeIdx(pub usize);

/// A list of [`Shape`]s paired with a clip rectangle.
Expand Down Expand Up @@ -151,6 +152,12 @@ impl PaintList {
self.0[idx.0] = ClippedShape { clip_rect, shape };
}

/// Set the given shape to be empty (a `Shape::Noop`).
#[inline(always)]
pub fn reset_shape(&mut self, idx: ShapeIdx) {
self.0[idx.0].shape = Shape::Noop;
}

/// Translate each [`Shape`] and clip rectangle by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
for ClippedShape { clip_rect, shape } in &mut self.0 {
Expand All @@ -165,20 +172,28 @@ impl PaintList {
}
}

/// This is where painted [`Shape`]s end up during a frame.
#[derive(Clone, Default)]
pub(crate) struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);
pub struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);

impl GraphicLayers {
pub fn list(&mut self, layer_id: LayerId) -> &mut PaintList {
/// Get or insert the [`PaintList`] for the given [`LayerId`].
pub fn entry(&mut self, layer_id: LayerId) -> &mut PaintList {
self.0[layer_id.order as usize]
.entry(layer_id.id)
.or_default()
}

/// Get the [`PaintList`] for the given [`LayerId`].
pub fn get(&self, layer_id: LayerId) -> Option<&PaintList> {
self.0[layer_id.order as usize].get(&layer_id.id)
}

/// Get the [`PaintList`] for the given [`LayerId`].
pub fn get_mut(&mut self, layer_id: LayerId) -> Option<&mut PaintList> {
self.0[layer_id.order as usize].get_mut(&layer_id.id)
}

pub fn drain(&mut self, area_order: &[LayerId]) -> Vec<ClippedShape> {
crate::profile_function!();

Expand Down
53 changes: 32 additions & 21 deletions crates/egui/src/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ impl Painter {
impl Painter {
#[inline]
fn paint_list<R>(&self, writer: impl FnOnce(&mut PaintList) -> R) -> R {
self.ctx.graphics_mut(|g| writer(g.list(self.layer_id)))
self.ctx.graphics_mut(|g| writer(g.entry(self.layer_id)))
}

fn transform_shape(&self, shape: &mut Shape) {
Expand Down Expand Up @@ -257,21 +257,21 @@ impl Painter {
/// # Paint different primitives
impl Painter {
/// Paints a line from the first point to the second.
pub fn line_segment(&self, points: [Pos2; 2], stroke: impl Into<Stroke>) {
pub fn line_segment(&self, points: [Pos2; 2], stroke: impl Into<Stroke>) -> ShapeIdx {
self.add(Shape::LineSegment {
points,
stroke: stroke.into(),
});
})
}

/// Paints a horizontal line.
pub fn hline(&self, x: impl Into<Rangef>, y: f32, stroke: impl Into<Stroke>) {
self.add(Shape::hline(x, y, stroke));
pub fn hline(&self, x: impl Into<Rangef>, y: f32, stroke: impl Into<Stroke>) -> ShapeIdx {
self.add(Shape::hline(x, y, stroke))
}

/// Paints a vertical line.
pub fn vline(&self, x: f32, y: impl Into<Rangef>, stroke: impl Into<Stroke>) {
self.add(Shape::vline(x, y, stroke));
pub fn vline(&self, x: f32, y: impl Into<Rangef>, stroke: impl Into<Stroke>) -> ShapeIdx {
self.add(Shape::vline(x, y, stroke))
}

pub fn circle(
Expand All @@ -280,31 +280,36 @@ impl Painter {
radius: f32,
fill_color: impl Into<Color32>,
stroke: impl Into<Stroke>,
) {
) -> ShapeIdx {
self.add(CircleShape {
center,
radius,
fill: fill_color.into(),
stroke: stroke.into(),
});
})
}

pub fn circle_filled(&self, center: Pos2, radius: f32, fill_color: impl Into<Color32>) {
pub fn circle_filled(
&self,
center: Pos2,
radius: f32,
fill_color: impl Into<Color32>,
) -> ShapeIdx {
self.add(CircleShape {
center,
radius,
fill: fill_color.into(),
stroke: Default::default(),
});
})
}

pub fn circle_stroke(&self, center: Pos2, radius: f32, stroke: impl Into<Stroke>) {
pub fn circle_stroke(&self, center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> ShapeIdx {
self.add(CircleShape {
center,
radius,
fill: Default::default(),
stroke: stroke.into(),
});
})
}

pub fn rect(
Expand All @@ -313,26 +318,26 @@ impl Painter {
rounding: impl Into<Rounding>,
fill_color: impl Into<Color32>,
stroke: impl Into<Stroke>,
) {
self.add(RectShape::new(rect, rounding, fill_color, stroke));
) -> ShapeIdx {
self.add(RectShape::new(rect, rounding, fill_color, stroke))
}

pub fn rect_filled(
&self,
rect: Rect,
rounding: impl Into<Rounding>,
fill_color: impl Into<Color32>,
) {
self.add(RectShape::filled(rect, rounding, fill_color));
) -> ShapeIdx {
self.add(RectShape::filled(rect, rounding, fill_color))
}

pub fn rect_stroke(
&self,
rect: Rect,
rounding: impl Into<Rounding>,
stroke: impl Into<Stroke>,
) {
self.add(RectShape::stroke(rect, rounding, stroke));
) -> ShapeIdx {
self.add(RectShape::stroke(rect, rounding, stroke))
}

/// Show an arrow starting at `origin` and going in the direction of `vec`, with the length `vec.length()`.
Expand Down Expand Up @@ -366,8 +371,14 @@ impl Painter {
/// .paint_at(ui, rect);
/// # });
/// ```
pub fn image(&self, texture_id: epaint::TextureId, rect: Rect, uv: Rect, tint: Color32) {
self.add(Shape::image(texture_id, rect, uv, tint));
pub fn image(
&self,
texture_id: epaint::TextureId,
rect: Rect,
uv: Rect,
tint: Color32,
) -> ShapeIdx {
self.add(Shape::image(texture_id, rect, uv, tint))
}
}

Expand Down
16 changes: 15 additions & 1 deletion crates/egui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,12 @@ pub struct Interaction {

/// Can you select the text on a [`crate::Label`] by default?
pub selectable_labels: bool,

/// Can the user select text that span multiple labels?
///
/// The default is `true`, but text seelction can be slightly glitchy,
/// so you may want to disable it.
pub multi_widget_text_select: bool,
}

/// Controls the visual style (colors etc) of egui.
Expand Down Expand Up @@ -1120,6 +1126,7 @@ impl Default for Interaction {
show_tooltips_only_when_still: true,
tooltip_delay: 0.0,
selectable_labels: true,
multi_widget_text_select: true,
}
}
}
Expand Down Expand Up @@ -1580,6 +1587,7 @@ impl Interaction {
show_tooltips_only_when_still,
tooltip_delay,
selectable_labels,
multi_widget_text_select,
} = self;
ui.add(Slider::new(resize_grab_radius_side, 0.0..=20.0).text("resize_grab_radius_side"));
ui.add(
Expand All @@ -1590,7 +1598,13 @@ impl Interaction {
"Only show tooltips if mouse is still",
);
ui.add(Slider::new(tooltip_delay, 0.0..=1.0).text("tooltip_delay"));
ui.checkbox(selectable_labels, "Selectable text in labels");

ui.horizontal(|ui| {
ui.checkbox(selectable_labels, "Selectable text in labels");
if *selectable_labels {
ui.checkbox(multi_widget_text_select, "Across multiple labels");
}
});

ui.vertical_centered(|ui| reset_button(ui, self));
}
Expand Down
6 changes: 3 additions & 3 deletions crates/egui/src/text_selection/cursor_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,10 @@ impl CCursorRange {
}

#[inline]
pub fn two(min: CCursor, max: CCursor) -> Self {
pub fn two(min: impl Into<CCursor>, max: impl Into<CCursor>) -> Self {
Self {
primary: max,
secondary: min,
primary: max.into(),
secondary: min.into(),
}
}

Expand Down
Loading
Loading