1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

Merge branch 'master' into cache_galley_lines

This commit is contained in:
Hubert Głuchowski
2025-03-26 18:52:43 +01:00
319 changed files with 10259 additions and 5112 deletions

View File

@@ -8,20 +8,39 @@ use crate::*;
pub struct RectShape {
pub rect: Rect,
/// How rounded the corners are. Use `Rounding::ZERO` for no rounding.
pub rounding: Rounding,
/// How rounded the corners of the rectangle are.
///
/// Use [`CornerRadius::ZERO`] for for sharp corners.
///
/// This is the corner radii of the rectangle.
/// If there is a stroke, then the stroke will have an inner and outer corner radius,
/// and those will depend on [`StrokeKind`] and the stroke width.
///
/// For [`StrokeKind::Inside`], the outside of the stroke coincides with the rectangle,
/// so the rounding will in this case specify the outer corner radius.
pub corner_radius: CornerRadius,
/// How to fill the rectangle.
pub fill: Color32,
/// The thickness and color of the outline.
///
/// The stroke extends _outside_ the edge of [`Self::rect`],
/// i.e. using [`crate::StrokeKind::Outside`].
///
/// This means the [`Self::visual_bounding_rect`] is `rect.size() + 2.0 * stroke.width`.
/// Whether or not the stroke is inside or outside the edge of [`Self::rect`],
/// is controlled by [`Self::stroke_kind`].
pub stroke: Stroke,
/// Is the stroke on the inside, outside, or centered on the rectangle?
///
/// If you want to perfectly tile rectangles, use [`StrokeKind::Inside`].
pub stroke_kind: StrokeKind,
/// Snap the rectangle to pixels?
///
/// Rounding produces sharper rectangles.
///
/// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.
pub round_to_pixels: Option<bool>,
/// If larger than zero, the edges of the rectangle
/// (for both fill and stroke) will be blurred.
///
@@ -50,19 +69,22 @@ fn rect_shape_size() {
}
impl RectShape {
/// The stroke extends _outside_ the [`Rect`].
/// See also [`Self::filled`] and [`Self::stroke`].
#[inline]
pub fn new(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
fill_color: impl Into<Color32>,
stroke: impl Into<Stroke>,
stroke_kind: StrokeKind,
) -> Self {
Self {
rect,
rounding: rounding.into(),
corner_radius: corner_radius.into(),
fill: fill_color.into(),
stroke: stroke.into(),
stroke_kind,
round_to_pixels: None,
blur_width: 0.0,
brush: Default::default(),
}
@@ -71,17 +93,45 @@ impl RectShape {
#[inline]
pub fn filled(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
fill_color: impl Into<Color32>,
) -> Self {
Self::new(rect, rounding, fill_color, Stroke::NONE)
Self::new(
rect,
corner_radius,
fill_color,
Stroke::NONE,
StrokeKind::Outside, // doesn't matter
)
}
/// The stroke extends _outside_ the [`Rect`].
#[inline]
pub fn stroke(rect: Rect, rounding: impl Into<Rounding>, stroke: impl Into<Stroke>) -> Self {
pub fn stroke(
rect: Rect,
corner_radius: impl Into<CornerRadius>,
stroke: impl Into<Stroke>,
stroke_kind: StrokeKind,
) -> Self {
let fill = Color32::TRANSPARENT;
Self::new(rect, rounding, fill, stroke)
Self::new(rect, corner_radius, fill, stroke, stroke_kind)
}
/// Set if the stroke is on the inside, outside, or centered on the rectangle.
#[inline]
pub fn with_stroke_kind(mut self, stroke_kind: StrokeKind) -> Self {
self.stroke_kind = stroke_kind;
self
}
/// Snap the rectangle to pixels?
///
/// Rounding produces sharper rectangles.
///
/// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.
#[inline]
pub fn with_round_to_pixels(mut self, round_to_pixels: bool) -> Self {
self.round_to_pixels = Some(round_to_pixels);
self
}
/// If larger than zero, the edges of the rectangle
@@ -112,8 +162,12 @@ impl RectShape {
if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
Rect::NOTHING
} else {
let Stroke { width, .. } = self.stroke; // Make sure we remember to update this if we change `stroke` to `PathStroke`
self.rect.expand(width + self.blur_width / 2.0)
let expand = match self.stroke_kind {
StrokeKind::Inside => 0.0,
StrokeKind::Middle => self.stroke.width / 2.0,
StrokeKind::Outside => self.stroke.width,
};
self.rect.expand(expand + self.blur_width / 2.0)
}
}

View File

@@ -7,7 +7,7 @@ use emath::{pos2, Align2, Pos2, Rangef, Rect, TSTransform, Vec2};
use crate::{
stroke::PathStroke,
text::{FontId, Fonts, Galley},
Color32, Mesh, Rounding, Stroke, TextureId,
Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId,
};
use super::{
@@ -275,23 +275,25 @@ impl Shape {
Self::Ellipse(EllipseShape::stroke(center, radius, stroke))
}
/// See also [`Self::rect_stroke`].
#[inline]
pub fn rect_filled(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
fill_color: impl Into<Color32>,
) -> Self {
Self::Rect(RectShape::filled(rect, rounding, fill_color))
Self::Rect(RectShape::filled(rect, corner_radius, fill_color))
}
/// The stroke extends _outside_ the [`Rect`].
/// See also [`Self::rect_filled`].
#[inline]
pub fn rect_stroke(
rect: Rect,
rounding: impl Into<Rounding>,
corner_radius: impl Into<CornerRadius>,
stroke: impl Into<Stroke>,
stroke_kind: StrokeKind,
) -> Self {
Self::Rect(RectShape::stroke(rect, rounding, stroke))
Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))
}
#[allow(clippy::needless_pass_by_value)]
@@ -337,7 +339,7 @@ impl Shape {
#[inline]
pub fn mesh(mesh: impl Into<Arc<Mesh>>) -> Self {
let mesh = mesh.into();
debug_assert!(mesh.is_valid());
debug_assert!(mesh.is_valid(), "Invalid mesh: {mesh:#?}");
Self::Mesh(mesh)
}
@@ -449,8 +451,9 @@ impl Shape {
}
Self::Rect(rect_shape) => {
rect_shape.rect = transform * rect_shape.rect;
rect_shape.corner_radius *= transform.scaling;
rect_shape.stroke.width *= transform.scaling;
rect_shape.rounding *= transform.scaling;
rect_shape.blur_width *= transform.scaling;
}
Self::Text(text_shape) => {
text_shape.pos = transform * text_shape.pos;
@@ -471,17 +474,17 @@ impl Shape {
Self::Mesh(mesh) => {
Arc::make_mut(mesh).transform(transform);
}
Self::QuadraticBezier(bezier_shape) => {
bezier_shape.points[0] = transform * bezier_shape.points[0];
bezier_shape.points[1] = transform * bezier_shape.points[1];
bezier_shape.points[2] = transform * bezier_shape.points[2];
bezier_shape.stroke.width *= transform.scaling;
}
Self::CubicBezier(cubic_curve) => {
for p in &mut cubic_curve.points {
Self::QuadraticBezier(bezier) => {
for p in &mut bezier.points {
*p = transform * *p;
}
cubic_curve.stroke.width *= transform.scaling;
bezier.stroke.width *= transform.scaling;
}
Self::CubicBezier(bezier) => {
for p in &mut bezier.points {
*p = transform * *p;
}
bezier.stroke.width *= transform.scaling;
}
Self::Callback(shape) => {
shape.rect = transform * shape.rect;
@@ -501,7 +504,7 @@ fn points_from_line(
shapes: &mut Vec<Shape>,
) {
let mut position_on_segment = 0.0;
path.windows(2).for_each(|window| {
for window in path.windows(2) {
let (start, end) = (window[0], window[1]);
let vector = end - start;
let segment_length = vector.length();
@@ -511,7 +514,7 @@ fn points_from_line(
position_on_segment += spacing;
}
position_on_segment -= segment_length;
});
}
}
/// Creates dashes from a line.
@@ -523,12 +526,18 @@ fn dashes_from_line(
shapes: &mut Vec<Shape>,
dash_offset: f32,
) {
assert_eq!(dash_lengths.len(), gap_lengths.len());
assert_eq!(
dash_lengths.len(),
gap_lengths.len(),
"Mismatched dash and gap lengths, got dash_lengths: {}, gap_lengths: {}",
dash_lengths.len(),
gap_lengths.len()
);
let mut position_on_segment = dash_offset;
let mut drawing_dash = false;
let mut step = 0;
let steps = dash_lengths.len();
path.windows(2).for_each(|window| {
for window in path.windows(2) {
let (start, end) = (window[0], window[1]);
let vector = end - start;
let segment_length = vector.length();
@@ -559,5 +568,5 @@ fn dashes_from_line(
}
position_on_segment -= segment_length;
});
}
}